Merge branch 'master' into index-lifecycle

This commit is contained in:
Gordon Brown 2018-10-02 13:43:46 -06:00
commit fb907706ec
95 changed files with 3542 additions and 860 deletions

View File

@ -42,11 +42,6 @@
<suppress files="client[/\\]rest-high-level[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]client[/\\]documentation[/\\]StoredScriptsDocumentationIT.java" id="SnippetLength" />
<suppress files="client[/\\]rest-high-level[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]client[/\\]documentation[/\\]TasksClientDocumentationIT.java" id="SnippetLength" />
<suppress files="client[/\\]rest-high-level[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]client[/\\]documentation[/\\]WatcherDocumentationIT.java" id="SnippetLength" />
<!--
This one is in plugins/examples/script-expert-scoring but we need to
suppress it like this because we build that project twice, once in for
real and once as a test for our build system. -->
<suppress files="src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]example[/\\]expertscript[/\\]ExpertScriptPlugin.java" id="SnippetLength" />
<suppress files="modules[/\\]reindex[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]client[/\\]documentation[/\\]ReindexDocumentationIT.jav" id="SnippetLength" />
<!-- Hopefully temporary suppression of LineLength on files that don't pass it. We should remove these when we the
@ -166,7 +161,6 @@
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]AcknowledgedRequestBuilder.java" checks="LineLength" />
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]MasterNodeOperationRequestBuilder.java" checks="LineLength" />
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]MasterNodeReadOperationRequestBuilder.java" checks="LineLength" />
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]TransportMasterNodeAction.java" checks="LineLength" />
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]info[/\\]ClusterInfoRequest.java" checks="LineLength" />
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]info[/\\]ClusterInfoRequestBuilder.java" checks="LineLength" />
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]info[/\\]TransportClusterInfoAction.java" checks="LineLength" />

View File

@ -492,9 +492,9 @@ public class RestHighLevelClient implements Closeable {
* @param listener the listener to be notified upon request completion
*/
public final void updateByQueryAsync(UpdateByQueryRequest updateByQueryRequest, RequestOptions options,
ActionListener<BulkByScrollResponse> listener) {
ActionListener<BulkByScrollResponse> listener) {
performRequestAsyncAndParseEntity(
updateByQueryRequest, RequestConverters::updateByQuery, options, BulkByScrollResponse::fromXContent, listener, emptySet()
updateByQueryRequest, RequestConverters::updateByQuery, options, BulkByScrollResponse::fromXContent, listener, emptySet()
);
}
@ -524,36 +524,38 @@ public class RestHighLevelClient implements Closeable {
public final void deleteByQueryAsync(DeleteByQueryRequest deleteByQueryRequest, RequestOptions options,
ActionListener<BulkByScrollResponse> listener) {
performRequestAsyncAndParseEntity(
deleteByQueryRequest, RequestConverters::deleteByQuery, options, BulkByScrollResponse::fromXContent, listener, emptySet()
deleteByQueryRequest, RequestConverters::deleteByQuery, options, BulkByScrollResponse::fromXContent, listener, emptySet()
);
}
/**
* Executes a reindex rethrottling request.
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-reindex.html#docs-reindex-rethrottle">
* Reindex rethrottling API on elastic.co</a>
* Reindex rethrottling API on elastic.co</a>
*
* @param rethrottleRequest the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public final ListTasksResponse reindexRethrottle(RethrottleRequest rethrottleRequest, RequestOptions options) throws IOException {
return performRequestAndParseEntity(rethrottleRequest, RequestConverters::rethrottle, options, ListTasksResponse::fromXContent,
emptySet());
emptySet());
}
/**
* Executes a reindex rethrottling request.
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-reindex.html#docs-reindex-rethrottle">
* Reindex rethrottling API on elastic.co</a>
* Reindex rethrottling API on elastic.co</a>
*
* @param rethrottleRequest the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
*/
public final void reindexRethrottleAsync(RethrottleRequest rethrottleRequest, RequestOptions options,
ActionListener<ListTasksResponse> listener) {
ActionListener<ListTasksResponse> listener) {
performRequestAsyncAndParseEntity(rethrottleRequest, RequestConverters::rethrottle, options, ListTasksResponse::fromXContent,
listener, emptySet());
listener, emptySet());
}
/**

View File

@ -20,6 +20,8 @@
package org.elasticsearch.client;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.client.rollup.GetRollupJobRequest;
import org.elasticsearch.client.rollup.GetRollupJobResponse;
import org.elasticsearch.client.rollup.PutRollupJobRequest;
import org.elasticsearch.client.rollup.PutRollupJobResponse;
@ -73,4 +75,37 @@ public class RollupClient {
PutRollupJobResponse::fromXContent,
listener, Collections.emptySet());
}
/**
* Get a rollup job from the cluster.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rollup-put-job.html">
* the docs</a> for more.
* @param request the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public GetRollupJobResponse getRollupJob(GetRollupJobRequest request, RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request,
RollupRequestConverters::getJob,
options,
GetRollupJobResponse::fromXContent,
Collections.emptySet());
}
/**
* Asynchronously get a rollup job from the cluster.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rollup-put-job.html">
* the docs</a> for more.
* @param request the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
*/
public void getRollupJobAsync(GetRollupJobRequest request, RequestOptions options, ActionListener<GetRollupJobResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request,
RollupRequestConverters::getJob,
options,
GetRollupJobResponse::fromXContent,
listener, Collections.emptySet());
}
}

View File

@ -18,7 +18,9 @@
*/
package org.elasticsearch.client;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.elasticsearch.client.rollup.GetRollupJobRequest;
import org.elasticsearch.client.rollup.PutRollupJobRequest;
import java.io.IOException;
@ -42,4 +44,14 @@ final class RollupRequestConverters {
request.setEntity(createEntity(putRollupJobRequest, REQUEST_BODY_CONTENT_TYPE));
return request;
}
static Request getJob(final GetRollupJobRequest getRollupJobRequest) {
String endpoint = new RequestConverters.EndpointBuilder()
.addPathPartAsIs("_xpack")
.addPathPartAsIs("rollup")
.addPathPartAsIs("job")
.addPathPart(getRollupJobRequest.getJobId())
.build();
return new Request(HttpGet.METHOD_NAME, endpoint);
}
}

View File

@ -25,6 +25,7 @@ import org.elasticsearch.client.security.EnableUserRequest;
import org.elasticsearch.client.security.PutUserRequest;
import org.elasticsearch.client.security.PutUserResponse;
import org.elasticsearch.client.security.EmptyResponse;
import org.elasticsearch.client.security.ChangePasswordRequest;
import java.io.IOException;
@ -47,6 +48,7 @@ public final class SecurityClient {
* Create/update a user in the native realm synchronously.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-users.html">
* the docs</a> for more.
*
* @param request the request with the user's information
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response from the put user call
@ -61,8 +63,9 @@ public final class SecurityClient {
* Asynchronously create/update a user in the native realm.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-users.html">
* the docs</a> for more.
* @param request the request with the user's information
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
*
* @param request the request with the user's information
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
*/
public void putUserAsync(PutUserRequest request, RequestOptions options, ActionListener<PutUserResponse> listener) {
@ -74,6 +77,7 @@ public final class SecurityClient {
* Enable a native realm or built-in user synchronously.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-enable-user.html">
* the docs</a> for more.
*
* @param request the request with the user to enable
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response from the enable user call
@ -88,12 +92,13 @@ public final class SecurityClient {
* Enable a native realm or built-in user asynchronously.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-enable-user.html">
* the docs</a> for more.
* @param request the request with the user to enable
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
*
* @param request the request with the user to enable
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
*/
public void enableUserAsync(EnableUserRequest request, RequestOptions options,
ActionListener<EmptyResponse> listener) {
ActionListener<EmptyResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::enableUser, options,
EmptyResponse::fromXContent, listener, emptySet());
}
@ -102,6 +107,7 @@ public final class SecurityClient {
* Disable a native realm or built-in user synchronously.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-disable-user.html">
* the docs</a> for more.
*
* @param request the request with the user to disable
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response from the enable user call
@ -116,13 +122,44 @@ public final class SecurityClient {
* Disable a native realm or built-in user asynchronously.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-disable-user.html">
* the docs</a> for more.
* @param request the request with the user to disable
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
*
* @param request the request with the user to disable
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
*/
public void disableUserAsync(DisableUserRequest request, RequestOptions options,
ActionListener<EmptyResponse> listener) {
ActionListener<EmptyResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::disableUser, options,
EmptyResponse::fromXContent, listener, emptySet());
}
/**
* Change the password of a user of a native realm or built-in user synchronously.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-change-password.html">
* the docs</a> for more.
*
* @param request the request with the user's new password
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response from the change user password call
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public EmptyResponse changePassword(ChangePasswordRequest request, RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::changePassword, options,
EmptyResponse::fromXContent, emptySet());
}
/**
* Change the password of a user of a native realm or built-in user asynchronously.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-change-password.html">
* the docs</a> for more.
*
* @param request the request with the user's new password
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
*/
public void changePasswordAsync(ChangePasswordRequest request, RequestOptions options,
ActionListener<EmptyResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::changePassword, options,
EmptyResponse::fromXContent, listener, emptySet());
}
}

View File

@ -19,9 +19,11 @@
package org.elasticsearch.client;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.elasticsearch.client.security.DisableUserRequest;
import org.elasticsearch.client.security.EnableUserRequest;
import org.elasticsearch.client.security.ChangePasswordRequest;
import org.elasticsearch.client.security.PutUserRequest;
import org.elasticsearch.client.security.SetUserEnabledRequest;
@ -34,6 +36,19 @@ final class SecurityRequestConverters {
private SecurityRequestConverters() {}
static Request changePassword(ChangePasswordRequest changePasswordRequest) throws IOException {
String endpoint = new RequestConverters.EndpointBuilder()
.addPathPartAsIs("_xpack/security/user")
.addPathPart(changePasswordRequest.getUsername())
.addPathPartAsIs("_password")
.build();
Request request = new Request(HttpPost.METHOD_NAME, endpoint);
request.setEntity(createEntity(changePasswordRequest, REQUEST_BODY_CONTENT_TYPE));
RequestConverters.Params params = new RequestConverters.Params(request);
params.withRefreshPolicy(changePasswordRequest.getRefreshPolicy());
return request;
}
static Request putUser(PutUserRequest putUserRequest) throws IOException {
String endpoint = new RequestConverters.EndpointBuilder()
.addPathPartAsIs("_xpack/security/user")

View File

@ -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.client.rollup;
import org.elasticsearch.client.Validatable;
import org.elasticsearch.client.ValidationException;
import java.util.Objects;
import java.util.Optional;
/**
* Request to fetch rollup jobs.
*/
public class GetRollupJobRequest implements Validatable {
private final String jobId;
/**
* Create a requets .
* @param jobId id of the job to return or {@code _all} to return all jobs
*/
public GetRollupJobRequest(final String jobId) {
Objects.requireNonNull(jobId, "jobId is required");
if ("_all".equals(jobId)) {
throw new IllegalArgumentException("use the default ctor to ask for all jobs");
}
this.jobId = jobId;
}
/**
* Create a request to load all rollup jobs.
*/
public GetRollupJobRequest() {
this.jobId = "_all";
}
/**
* ID of the job to return.
*/
public String getJobId() {
return jobId;
}
@Override
public Optional<ValidationException> validate() {
return Optional.empty();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final GetRollupJobRequest that = (GetRollupJobRequest) o;
return jobId.equals(that.jobId);
}
@Override
public int hashCode() {
return Objects.hash(jobId);
}
}

View File

@ -0,0 +1,374 @@
/*
* 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.rollup;
import org.elasticsearch.client.rollup.job.config.RollupJobConfig;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.Objects;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
import static java.util.Collections.unmodifiableList;
import static java.util.stream.Collectors.joining;
/**
* Response from rollup's get jobs api.
*/
public class GetRollupJobResponse {
static final ParseField JOBS = new ParseField("jobs");
static final ParseField CONFIG = new ParseField("config");
static final ParseField STATS = new ParseField("stats");
static final ParseField STATUS = new ParseField("status");
static final ParseField NUM_PAGES = new ParseField("pages_processed");
static final ParseField NUM_INPUT_DOCUMENTS = new ParseField("documents_processed");
static final ParseField NUM_OUTPUT_DOCUMENTS = new ParseField("rollups_indexed");
static final ParseField NUM_INVOCATIONS = new ParseField("trigger_count");
static final ParseField STATE = new ParseField("job_state");
static final ParseField CURRENT_POSITION = new ParseField("current_position");
static final ParseField UPGRADED_DOC_ID = new ParseField("upgraded_doc_id");
private List<JobWrapper> jobs;
GetRollupJobResponse(final List<JobWrapper> jobs) {
this.jobs = Objects.requireNonNull(jobs, "jobs is required");
}
/**
* Jobs returned by the request.
*/
public List<JobWrapper> getJobs() {
return jobs;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final GetRollupJobResponse that = (GetRollupJobResponse) o;
return jobs.equals(that.jobs);
}
@Override
public int hashCode() {
return Objects.hash(jobs);
}
private static final ConstructingObjectParser<GetRollupJobResponse, Void> PARSER = new ConstructingObjectParser<>(
"get_rollup_job_response",
true,
args -> {
@SuppressWarnings("unchecked") // We're careful about the type in the list
List<JobWrapper> jobs = (List<JobWrapper>) args[0];
return new GetRollupJobResponse(unmodifiableList(jobs));
});
static {
PARSER.declareObjectArray(constructorArg(), JobWrapper.PARSER::apply, JOBS);
}
public static GetRollupJobResponse fromXContent(final XContentParser parser) throws IOException {
return PARSER.parse(parser, null);
}
@Override
public final String toString() {
return "{jobs=" + jobs.stream().map(Object::toString).collect(joining("\n")) + "\n}";
}
public static class JobWrapper {
private final RollupJobConfig job;
private final RollupIndexerJobStats stats;
private final RollupJobStatus status;
JobWrapper(RollupJobConfig job, RollupIndexerJobStats stats, RollupJobStatus status) {
this.job = job;
this.stats = stats;
this.status = status;
}
/**
* Configuration of the job.
*/
public RollupJobConfig getJob() {
return job;
}
/**
* Statistics about the execution of the job.
*/
public RollupIndexerJobStats getStats() {
return stats;
}
/**
* Current state of the job.
*/
public RollupJobStatus getStatus() {
return status;
}
private static final ConstructingObjectParser<JobWrapper, Void> PARSER = new ConstructingObjectParser<>(
"job",
true,
a -> new JobWrapper((RollupJobConfig) a[0], (RollupIndexerJobStats) a[1], (RollupJobStatus) a[2]));
static {
PARSER.declareObject(ConstructingObjectParser.constructorArg(), (p, c) -> RollupJobConfig.fromXContent(p, null), CONFIG);
PARSER.declareObject(ConstructingObjectParser.constructorArg(), RollupIndexerJobStats.PARSER::apply, STATS);
PARSER.declareObject(ConstructingObjectParser.constructorArg(), RollupJobStatus.PARSER::apply, STATUS);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
JobWrapper other = (JobWrapper) obj;
return Objects.equals(job, other.job)
&& Objects.equals(stats, other.stats)
&& Objects.equals(status, other.status);
}
@Override
public int hashCode() {
return Objects.hash(job, stats, status);
}
@Override
public final String toString() {
return "{job=" + job
+ ", stats=" + stats
+ ", status=" + status + "}";
}
}
/**
* The Rollup specialization of stats for the AsyncTwoPhaseIndexer.
* Note: instead of `documents_indexed`, this XContent show `rollups_indexed`
*/
public static class RollupIndexerJobStats {
private final long numPages;
private final long numInputDocuments;
private final long numOuputDocuments;
private final long numInvocations;
RollupIndexerJobStats(long numPages, long numInputDocuments, long numOuputDocuments, long numInvocations) {
this.numPages = numPages;
this.numInputDocuments = numInputDocuments;
this.numOuputDocuments = numOuputDocuments;
this.numInvocations = numInvocations;
}
/**
* The number of pages read from the input indices.
*/
public long getNumPages() {
return numPages;
}
/**
* The number of documents read from the input indices.
*/
public long getNumDocuments() {
return numInputDocuments;
}
/**
* Number of times that the job woke up to write documents.
*/
public long getNumInvocations() {
return numInvocations;
}
/**
* Number of documents written to the result indices.
*/
public long getOutputDocuments() {
return numOuputDocuments;
}
private static final ConstructingObjectParser<RollupIndexerJobStats, Void> PARSER = new ConstructingObjectParser<>(
STATS.getPreferredName(),
true,
args -> new RollupIndexerJobStats((long) args[0], (long) args[1], (long) args[2], (long) args[3]));
static {
PARSER.declareLong(constructorArg(), NUM_PAGES);
PARSER.declareLong(constructorArg(), NUM_INPUT_DOCUMENTS);
PARSER.declareLong(constructorArg(), NUM_OUTPUT_DOCUMENTS);
PARSER.declareLong(constructorArg(), NUM_INVOCATIONS);
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
RollupIndexerJobStats that = (RollupIndexerJobStats) other;
return Objects.equals(this.numPages, that.numPages)
&& Objects.equals(this.numInputDocuments, that.numInputDocuments)
&& Objects.equals(this.numOuputDocuments, that.numOuputDocuments)
&& Objects.equals(this.numInvocations, that.numInvocations);
}
@Override
public int hashCode() {
return Objects.hash(numPages, numInputDocuments, numOuputDocuments, numInvocations);
}
@Override
public final String toString() {
return "{pages=" + numPages
+ ", input_docs=" + numInputDocuments
+ ", output_docs=" + numOuputDocuments
+ ", invocations=" + numInvocations + "}";
}
}
/**
* Status of the rollup job.
*/
public static class RollupJobStatus {
private final IndexerState state;
private final Map<String, Object> currentPosition;
private final boolean upgradedDocumentId;
RollupJobStatus(IndexerState state, Map<String, Object> position, boolean upgradedDocumentId) {
this.state = state;
this.currentPosition = position;
this.upgradedDocumentId = upgradedDocumentId;
}
/**
* The state of the writer.
*/
public IndexerState getState() {
return state;
}
/**
* The current position of the writer.
*/
public Map<String, Object> getCurrentPosition() {
return currentPosition;
}
/**
* Flag holds the state of the ID scheme, e.g. if it has been upgraded
* to the concatenation scheme.
*/
public boolean getUpgradedDocumentId() {
return upgradedDocumentId;
}
private static final ConstructingObjectParser<RollupJobStatus, Void> PARSER = new ConstructingObjectParser<>(
STATUS.getPreferredName(),
true,
args -> {
IndexerState state = (IndexerState) args[0];
@SuppressWarnings("unchecked") // We're careful of the contents
Map<String, Object> currentPosition = (Map<String, Object>) args[1];
Boolean upgradedDocumentId = (Boolean) args[2];
return new RollupJobStatus(state, currentPosition, upgradedDocumentId == null ? false : upgradedDocumentId);
});
static {
PARSER.declareField(constructorArg(), p -> IndexerState.fromString(p.text()), STATE, ObjectParser.ValueType.STRING);
PARSER.declareField(optionalConstructorArg(), p -> {
if (p.currentToken() == XContentParser.Token.START_OBJECT) {
return p.map();
}
if (p.currentToken() == XContentParser.Token.VALUE_NULL) {
return null;
}
throw new IllegalArgumentException("Unsupported token [" + p.currentToken() + "]");
}, CURRENT_POSITION, ObjectParser.ValueType.VALUE_OBJECT_ARRAY);
// Optional to accommodate old versions of state
PARSER.declareBoolean(ConstructingObjectParser.optionalConstructorArg(), UPGRADED_DOC_ID);
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
RollupJobStatus that = (RollupJobStatus) other;
return Objects.equals(state, that.state)
&& Objects.equals(currentPosition, that.currentPosition)
&& upgradedDocumentId == that.upgradedDocumentId;
}
@Override
public int hashCode() {
return Objects.hash(state, currentPosition, upgradedDocumentId);
}
@Override
public final String toString() {
return "{stats=" + state
+ ", currentPosition=" + currentPosition
+ ", upgradedDocumentId=" + upgradedDocumentId + "}";
}
}
/**
* IndexerState represents the internal state of the indexer. It
* is also persistent when changing from started/stopped in case the allocated
* task is restarted elsewhere.
*/
public enum IndexerState {
/** Indexer is running, but not actively indexing data (e.g. it's idle). */
STARTED,
/** Indexer is actively indexing data. */
INDEXING,
/**
* Transition state to where an indexer has acknowledged the stop
* but is still in process of halting.
*/
STOPPING,
/** Indexer is "paused" and ignoring scheduled triggers. */
STOPPED,
/**
* Something (internal or external) has requested the indexer abort
* and shutdown.
*/
ABORTING;
static IndexerState fromString(String name) {
return valueOf(name.trim().toUpperCase(Locale.ROOT));
}
String value() {
return name().toLowerCase(Locale.ROOT);
}
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.security;
import org.elasticsearch.client.Validatable;
import org.elasticsearch.common.CharArrays;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
/**
* Request object to change the password of a user of a native realm or a built-in user.
*/
public final class ChangePasswordRequest implements Validatable, ToXContentObject {
private final String username;
private final char[] password;
private final RefreshPolicy refreshPolicy;
/**
* @param username The username of the user whose password should be changed or null for the current user.
* @param password The new password. The password array is not cleared by the {@link ChangePasswordRequest} object so the
* calling code must clear it after receiving the response.
* @param refreshPolicy The refresh policy for the request.
*/
public ChangePasswordRequest(@Nullable String username, char[] password, RefreshPolicy refreshPolicy) {
this.username = username;
this.password = Objects.requireNonNull(password, "password is required");
this.refreshPolicy = refreshPolicy == null ? RefreshPolicy.getDefault() : refreshPolicy;
}
public String getUsername() {
return username;
}
public char[] getPassword() {
return password;
}
public RefreshPolicy getRefreshPolicy() {
return refreshPolicy;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
byte[] charBytes = CharArrays.toUtf8Bytes(password);
try {
return builder.startObject()
.field("password").utf8Value(charBytes, 0, charBytes.length)
.endObject();
} finally {
Arrays.fill(charBytes, (byte) 0);
}
}
}

View File

@ -25,7 +25,6 @@ import org.elasticsearch.common.CharArrays;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.Closeable;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
@ -37,7 +36,7 @@ import java.util.Optional;
/**
* Request object to create or update a user in the native realm.
*/
public final class PutUserRequest implements Validatable, Closeable, ToXContentObject {
public final class PutUserRequest implements Validatable, ToXContentObject {
private final String username;
private final List<String> roles;
@ -48,6 +47,20 @@ public final class PutUserRequest implements Validatable, Closeable, ToXContentO
private final boolean enabled;
private final RefreshPolicy refreshPolicy;
/**
* Creates a new request that is used to create or update a user in the native realm.
*
* @param username the username of the user to be created or updated
* @param password the password of the user. The password array is not modified by this class.
* It is the responsibility of the caller to clear the password after receiving
* a response.
* @param roles the roles that this user is assigned
* @param fullName the full name of the user that may be used for display purposes
* @param email the email address of the user
* @param enabled true if the user is enabled and allowed to access elasticsearch
* @param metadata a map of additional user attributes that may be used in templating roles
* @param refreshPolicy the refresh policy for the request.
*/
public PutUserRequest(String username, char[] password, List<String> roles, String fullName, String email, boolean enabled,
Map<String, Object> metadata, RefreshPolicy refreshPolicy) {
this.username = Objects.requireNonNull(username, "username is required");
@ -114,13 +127,6 @@ public final class PutUserRequest implements Validatable, Closeable, ToXContentO
return result;
}
@Override
public void close() {
if (password != null) {
Arrays.fill(password, (char) 0);
}
}
@Override
public Optional<ValidationException> validate() {
if (metadata != null && metadata.keySet().stream().anyMatch(s -> s.startsWith("_"))) {
@ -137,7 +143,11 @@ public final class PutUserRequest implements Validatable, Closeable, ToXContentO
builder.field("username", username);
if (password != null) {
byte[] charBytes = CharArrays.toUtf8Bytes(password);
builder.field("password").utf8Value(charBytes, 0, charBytes.length);
try {
builder.field("password").utf8Value(charBytes, 0, charBytes.length);
} finally {
Arrays.fill(charBytes, (byte) 0);
}
}
if (roles != null) {
builder.field("roles", roles);

View File

@ -90,7 +90,7 @@ public class RankEvalIT extends ESRestHighLevelClientTestCase {
if (id.equals("berlin") || id.equals("amsterdam5")) {
assertFalse(hit.getRating().isPresent());
} else {
assertEquals(1, hit.getRating().get().intValue());
assertEquals(1, hit.getRating().getAsInt());
}
}
EvalQueryQuality berlinQueryQuality = partialResults.get("berlin_query");
@ -100,7 +100,7 @@ public class RankEvalIT extends ESRestHighLevelClientTestCase {
for (RatedSearchHit hit : hitsAndRatings) {
String id = hit.getSearchHit().getId();
if (id.equals("berlin")) {
assertEquals(1, hit.getRating().get().intValue());
assertEquals(1, hit.getRating().getAsInt());
} else {
assertFalse(hit.getRating().isPresent());
}

View File

@ -27,6 +27,10 @@ import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.client.rollup.GetRollupJobRequest;
import org.elasticsearch.client.rollup.GetRollupJobResponse;
import org.elasticsearch.client.rollup.GetRollupJobResponse.IndexerState;
import org.elasticsearch.client.rollup.GetRollupJobResponse.JobWrapper;
import org.elasticsearch.client.rollup.PutRollupJobRequest;
import org.elasticsearch.client.rollup.PutRollupJobResponse;
import org.elasticsearch.client.rollup.job.config.DateHistogramGroupConfig;
@ -50,6 +54,13 @@ import java.util.Locale;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.lessThan;
public class RollupIT extends ESRestHighLevelClientTestCase {
@ -57,7 +68,7 @@ public class RollupIT extends ESRestHighLevelClientTestCase {
SumAggregationBuilder.NAME, AvgAggregationBuilder.NAME, ValueCountAggregationBuilder.NAME);
@SuppressWarnings("unchecked")
public void testPutRollupJob() throws Exception {
public void testPutAndGetRollupJob() throws Exception {
double sum = 0.0d;
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
@ -90,7 +101,7 @@ public class RollupIT extends ESRestHighLevelClientTestCase {
BulkResponse bulkResponse = highLevelClient().bulk(bulkRequest, RequestOptions.DEFAULT);
assertEquals(RestStatus.OK, bulkResponse.status());
if (bulkResponse.hasFailures()) {
if (bulkResponse.hasFailures()) {
for (BulkItemResponse itemResponse : bulkResponse.getItems()) {
if (itemResponse.isFailed()) {
logger.fatal(itemResponse.getFailureMessage());
@ -158,5 +169,26 @@ public class RollupIT extends ESRestHighLevelClientTestCase {
}
}
});
// TODO when we move cleaning rollup into ESTestCase we can randomly choose the _all version of this request
GetRollupJobRequest getRollupJobRequest = new GetRollupJobRequest(id);
GetRollupJobResponse getResponse = execute(getRollupJobRequest, rollupClient::getRollupJob, rollupClient::getRollupJobAsync);
assertThat(getResponse.getJobs(), hasSize(1));
JobWrapper job = getResponse.getJobs().get(0);
assertEquals(putRollupJobRequest.getConfig(), job.getJob());
assertThat(job.getStats().getNumPages(), lessThan(10L));
assertEquals(numDocs, job.getStats().getNumDocuments());
assertThat(job.getStats().getNumInvocations(), greaterThan(0L));
assertEquals(1, job.getStats().getOutputDocuments());
assertThat(job.getStatus().getState(), either(equalTo(IndexerState.STARTED)).or(equalTo(IndexerState.INDEXING)));
assertThat(job.getStatus().getCurrentPosition(), hasKey("date.date_histogram"));
assertEquals(true, job.getStatus().getUpgradedDocumentId());
}
public void testGetMissingRollupJob() throws Exception {
GetRollupJobRequest getRollupJobRequest = new GetRollupJobRequest("missing");
RollupClient rollupClient = highLevelClient().rollup();
GetRollupJobResponse getResponse = execute(getRollupJobRequest, rollupClient::getRollupJob, rollupClient::getRollupJobAsync);
assertThat(getResponse.getJobs(), empty());
}
}

View File

@ -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.client;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.elasticsearch.client.rollup.GetRollupJobRequest;
import org.elasticsearch.client.rollup.PutRollupJobRequest;
import org.elasticsearch.client.rollup.job.config.RollupJobConfig;
import org.elasticsearch.client.rollup.job.config.RollupJobConfigTests;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.nullValue;
public class RollupRequestConvertersTests extends ESTestCase {
public void testPutJob() throws IOException {
String job = randomAlphaOfLength(5);
RollupJobConfig config = RollupJobConfigTests.randomRollupJobConfig(job);
PutRollupJobRequest put = new PutRollupJobRequest(config);
Request request = RollupRequestConverters.putJob(put);
assertThat(request.getEndpoint(), equalTo("/_xpack/rollup/job/" + job));
assertThat(HttpPut.METHOD_NAME, equalTo(request.getMethod()));
assertThat(request.getParameters().keySet(), empty());
RequestConvertersTests.assertToXContentBody(put, request.getEntity());
}
public void testGetJob() {
boolean getAll = randomBoolean();
String job = getAll ? "_all" : RequestConvertersTests.randomIndicesNames(1, 1)[0];
GetRollupJobRequest get = getAll ? new GetRollupJobRequest() : new GetRollupJobRequest(job);
Request request = RollupRequestConverters.getJob(get);
assertThat(request.getEndpoint(), equalTo("/_xpack/rollup/job/" + job));
assertThat(HttpGet.METHOD_NAME, equalTo(request.getMethod()));
assertThat(request.getParameters().keySet(), empty());
assertThat(request.getEntity(), nullValue());
}
}

View File

@ -19,9 +19,11 @@
package org.elasticsearch.client;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.elasticsearch.client.security.DisableUserRequest;
import org.elasticsearch.client.security.EnableUserRequest;
import org.elasticsearch.client.security.ChangePasswordRequest;
import org.elasticsearch.client.security.PutUserRequest;
import org.elasticsearch.client.security.RefreshPolicy;
import org.elasticsearch.test.ESTestCase;
@ -91,9 +93,34 @@ public class SecurityRequestConvertersTests extends ESTestCase {
private static Map<String, String> getExpectedParamsFromRefreshPolicy(RefreshPolicy refreshPolicy) {
if (refreshPolicy != RefreshPolicy.NONE) {
return Collections.singletonMap("refresh", refreshPolicy.getValue());
return Collections.singletonMap("refresh", refreshPolicy.getValue());
} else {
return Collections.emptyMap();
}
}
public void testChangePassword() throws IOException {
final String username = randomAlphaOfLengthBetween(4, 12);
final char[] password = randomAlphaOfLengthBetween(8, 12).toCharArray();
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
final Map<String, String> expectedParams = getExpectedParamsFromRefreshPolicy(refreshPolicy);
ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest(username, password, refreshPolicy);
Request request = SecurityRequestConverters.changePassword(changePasswordRequest);
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
assertEquals("/_xpack/security/user/" + changePasswordRequest.getUsername() + "/_password", request.getEndpoint());
assertEquals(expectedParams, request.getParameters());
assertToXContentBody(changePasswordRequest, request.getEntity());
}
public void testSelfChangePassword() throws IOException {
final char[] password = randomAlphaOfLengthBetween(8, 12).toCharArray();
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
final Map<String, String> expectedParams = getExpectedParamsFromRefreshPolicy(refreshPolicy);
ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest(null, password, refreshPolicy);
Request request = SecurityRequestConverters.changePassword(changePasswordRequest);
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
assertEquals("/_xpack/security/user/_password", request.getEndpoint());
assertEquals(expectedParams, request.getParameters());
assertToXContentBody(changePasswordRequest, request.getEntity());
}
}

View File

@ -27,8 +27,15 @@ import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.rollup.GetRollupJobRequest;
import org.elasticsearch.client.rollup.GetRollupJobResponse;
import org.elasticsearch.client.rollup.GetRollupJobResponse.JobWrapper;
import org.elasticsearch.client.rollup.GetRollupJobResponse.RollupIndexerJobStats;
import org.elasticsearch.client.rollup.GetRollupJobResponse.RollupJobStatus;
import org.elasticsearch.client.rollup.PutRollupJobRequest;
import org.elasticsearch.client.rollup.PutRollupJobResponse;
import org.elasticsearch.client.rollup.job.config.DateHistogramGroupConfig;
@ -38,19 +45,26 @@ import org.elasticsearch.client.rollup.job.config.MetricConfig;
import org.elasticsearch.client.rollup.job.config.RollupJobConfig;
import org.elasticsearch.client.rollup.job.config.TermsGroupConfig;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.junit.After;
import org.junit.Before;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.hasSize;
public class RollupDocumentationIT extends ESRestHighLevelClientTestCase {
@ -160,4 +174,110 @@ public class RollupDocumentationIT extends ESRestHighLevelClientTestCase {
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
}
public void testGetRollupJob() throws Exception {
testCreateRollupJob();
RestHighLevelClient client = highLevelClient();
// tag::x-pack-rollup-get-rollup-job-request
GetRollupJobRequest getAll = new GetRollupJobRequest(); // <1>
GetRollupJobRequest getJob = new GetRollupJobRequest("job_1"); // <2>
// end::x-pack-rollup-get-rollup-job-request
// tag::x-pack-rollup-get-rollup-job-execute
GetRollupJobResponse response = client.rollup().getRollupJob(getJob, RequestOptions.DEFAULT);
// end::x-pack-rollup-get-rollup-job-execute
// tag::x-pack-rollup-get-rollup-job-response
assertThat(response.getJobs(), hasSize(1));
JobWrapper job = response.getJobs().get(0); // <1>
RollupJobConfig config = job.getJob();
RollupJobStatus status = job.getStatus();
RollupIndexerJobStats stats = job.getStats();
// end::x-pack-rollup-get-rollup-job-response
assertNotNull(config);
assertNotNull(status);
assertNotNull(status);
// tag::x-pack-rollup-get-rollup-job-execute-listener
ActionListener<GetRollupJobResponse> listener = new ActionListener<GetRollupJobResponse>() {
@Override
public void onResponse(GetRollupJobResponse response) {
// <1>
}
@Override
public void onFailure(Exception e) {
// <2>
}
};
// end::x-pack-rollup-get-rollup-job-execute-listener
// Replace the empty listener by a blocking listener in test
final CountDownLatch latch = new CountDownLatch(1);
listener = new LatchedActionListener<>(listener, latch);
// tag::x-pack-rollup-get-rollup-job-execute-async
client.rollup().getRollupJobAsync(getJob, RequestOptions.DEFAULT, listener); // <1>
// end::x-pack-rollup-get-rollup-job-execute-async
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
@After
public void wipeRollup() throws Exception {
// TODO move this to ESRestTestCase
deleteRollupJobs();
waitForPendingRollupTasks();
}
private void deleteRollupJobs() throws Exception {
Response response = adminClient().performRequest(new Request("GET", "/_xpack/rollup/job/_all"));
Map<String, Object> jobs = entityAsMap(response);
@SuppressWarnings("unchecked")
List<Map<String, Object>> jobConfigs =
(List<Map<String, Object>>) XContentMapValues.extractValue("jobs", jobs);
if (jobConfigs == null) {
return;
}
for (Map<String, Object> jobConfig : jobConfigs) {
@SuppressWarnings("unchecked")
String jobId = (String) ((Map<String, Object>) jobConfig.get("config")).get("id");
Request request = new Request("DELETE", "/_xpack/rollup/job/" + jobId);
request.addParameter("ignore", "404"); // Ignore 404s because they imply someone was racing us to delete this
adminClient().performRequest(request);
}
}
private void waitForPendingRollupTasks() throws Exception {
assertBusy(() -> {
try {
Request request = new Request("GET", "/_cat/tasks");
request.addParameter("detailed", "true");
Response response = adminClient().performRequest(request);
try (BufferedReader responseReader = new BufferedReader(
new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8))) {
int activeTasks = 0;
String line;
StringBuilder tasksListString = new StringBuilder();
while ((line = responseReader.readLine()) != null) {
// We only care about Rollup jobs, otherwise this fails too easily due to unrelated tasks
if (line.startsWith("xpack/rollup/job") == true) {
activeTasks++;
tasksListString.append(line).append('\n');
}
}
assertEquals(activeTasks + " active tasks found:\n" + tasksListString, 0, activeTasks);
}
} catch (IOException e) {
// Throw an assertion error so we retry
throw new AssertionError("Error getting active tasks list", e);
}
});
}
}

View File

@ -24,6 +24,7 @@ import org.elasticsearch.action.LatchedActionListener;
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.security.ChangePasswordRequest;
import org.elasticsearch.client.security.DisableUserRequest;
import org.elasticsearch.client.security.EnableUserRequest;
import org.elasticsearch.client.security.PutUserRequest;
@ -42,7 +43,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
{
//tag::put-user-execute
char[] password = new char[] { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };
char[] password = new char[]{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
PutUserRequest request =
new PutUserRequest("example", password, Collections.singletonList("superuser"), null, null, true, null, RefreshPolicy.NONE);
PutUserResponse response = client.security().putUser(request, RequestOptions.DEFAULT);
@ -56,7 +57,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
}
{
char[] password = new char[] { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };
char[] password = new char[]{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
PutUserRequest request = new PutUserRequest("example2", password, Collections.singletonList("superuser"), null, null, true,
null, RefreshPolicy.NONE);
// tag::put-user-execute-listener
@ -173,4 +174,48 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
}
public void testChangePassword() throws Exception {
RestHighLevelClient client = highLevelClient();
char[] password = new char[]{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
char[] newPassword = new char[]{'n', 'e', 'w', 'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
PutUserRequest putUserRequest = new PutUserRequest("change_password_user", password, Collections.singletonList("superuser"),
null, null, true, null, RefreshPolicy.NONE);
PutUserResponse putUserResponse = client.security().putUser(putUserRequest, RequestOptions.DEFAULT);
assertTrue(putUserResponse.isCreated());
{
//tag::change-password-execute
ChangePasswordRequest request = new ChangePasswordRequest("change_password_user", newPassword, RefreshPolicy.NONE);
EmptyResponse response = client.security().changePassword(request, RequestOptions.DEFAULT);
//end::change-password-execute
assertNotNull(response);
}
{
//tag::change-password-execute-listener
ChangePasswordRequest request = new ChangePasswordRequest("change_password_user", password, RefreshPolicy.NONE);
ActionListener<EmptyResponse> listener = new ActionListener<EmptyResponse>() {
@Override
public void onResponse(EmptyResponse emptyResponse) {
// <1>
}
@Override
public void onFailure(Exception e) {
// <2>
}
};
//end::change-password-execute-listener
// Replace the empty listener by a blocking listener in test
final CountDownLatch latch = new CountDownLatch(1);
listener = new LatchedActionListener<>(listener, latch);
//tag::change-password-execute-async
client.security().changePasswordAsync(request, RequestOptions.DEFAULT, listener); // <1>
//end::change-password-execute-async
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
}
}

View File

@ -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.client.rollup;
import org.elasticsearch.test.ESTestCase;
public class GetRollupJobRequestTests extends ESTestCase {
public void testRequiresJob() {
final NullPointerException e = expectThrows(NullPointerException.class, () -> new GetRollupJobRequest(null));
assertEquals("jobId is required", e.getMessage());
}
public void testDoNotUseAll() {
final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new GetRollupJobRequest("_all"));
assertEquals("use the default ctor to ask for all jobs", e.getMessage());
}
}

View File

@ -0,0 +1,120 @@
/*
* 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.rollup;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.client.rollup.GetRollupJobResponse.IndexerState;
import org.elasticsearch.client.rollup.GetRollupJobResponse.JobWrapper;
import org.elasticsearch.client.rollup.GetRollupJobResponse.RollupIndexerJobStats;
import org.elasticsearch.client.rollup.GetRollupJobResponse.RollupJobStatus;
import org.elasticsearch.client.rollup.job.config.RollupJobConfigTests;
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.test.AbstractXContentTestCase.xContentTester;
public class GetRollupJobResponseTests extends ESTestCase {
public void testFromXContent() throws IOException {
xContentTester(
this::createParser,
this::createTestInstance,
this::toXContent,
GetRollupJobResponse::fromXContent)
.supportsUnknownFields(true)
.randomFieldsExcludeFilter(field ->
field.endsWith("status.current_position"))
.test();
}
private GetRollupJobResponse createTestInstance() {
int jobCount = between(1, 5);
List<JobWrapper> jobs = new ArrayList<>();
for (int j = 0; j < jobCount; j++) {
jobs.add(new JobWrapper(
RollupJobConfigTests.randomRollupJobConfig(randomAlphaOfLength(5)),
randomStats(),
randomStatus()));
}
return new GetRollupJobResponse(jobs);
}
private RollupIndexerJobStats randomStats() {
return new RollupIndexerJobStats(randomLong(), randomLong(), randomLong(), randomLong());
}
private RollupJobStatus randomStatus() {
Map<String, Object> currentPosition = new HashMap<>();
int positions = between(0, 10);
while (currentPosition.size() < positions) {
currentPosition.put(randomAlphaOfLength(2), randomAlphaOfLength(2));
}
return new RollupJobStatus(
randomFrom(IndexerState.values()),
currentPosition,
randomBoolean());
}
private void toXContent(GetRollupJobResponse response, XContentBuilder builder) throws IOException {
ToXContent.Params params = ToXContent.EMPTY_PARAMS;
builder.startObject();
builder.startArray(GetRollupJobResponse.JOBS.getPreferredName());
for (JobWrapper job : response.getJobs()) {
toXContent(job, builder, params);
}
builder.endArray();
builder.endObject();
}
private void toXContent(JobWrapper jobWrapper, XContentBuilder builder, ToXContent.Params params) throws IOException {
builder.startObject();
builder.field(GetRollupJobResponse.CONFIG.getPreferredName());
jobWrapper.getJob().toXContent(builder, params);
builder.field(GetRollupJobResponse.STATUS.getPreferredName());
toXContent(jobWrapper.getStatus(), builder, params);
builder.field(GetRollupJobResponse.STATS.getPreferredName());
toXContent(jobWrapper.getStats(), builder, params);
builder.endObject();
}
public void toXContent(RollupJobStatus status, XContentBuilder builder, ToXContent.Params params) throws IOException {
builder.startObject();
builder.field(GetRollupJobResponse.STATE.getPreferredName(), status.getState().value());
if (status.getCurrentPosition() != null) {
builder.field(GetRollupJobResponse.CURRENT_POSITION.getPreferredName(), status.getCurrentPosition());
}
builder.field(GetRollupJobResponse.UPGRADED_DOC_ID.getPreferredName(), status.getUpgradedDocumentId());
builder.endObject();
}
public void toXContent(RollupIndexerJobStats stats, XContentBuilder builder, ToXContent.Params params) throws IOException {
builder.startObject();
builder.field(GetRollupJobResponse.NUM_PAGES.getPreferredName(), stats.getNumPages());
builder.field(GetRollupJobResponse.NUM_INPUT_DOCUMENTS.getPreferredName(), stats.getNumDocuments());
builder.field(GetRollupJobResponse.NUM_OUTPUT_DOCUMENTS.getPreferredName(), stats.getOutputDocuments());
builder.field(GetRollupJobResponse.NUM_INVOCATIONS.getPreferredName(), stats.getNumInvocations());
builder.endObject();
}
}

View File

@ -173,7 +173,7 @@ if not "%SERVICE_USERNAME%" == "" (
)
)
"%EXECUTABLE%" //IS//%SERVICE_ID% --Startup %ES_START_TYPE% --StopTimeout %ES_STOP_TIMEOUT% --StartClass org.elasticsearch.bootstrap.Elasticsearch --StartMethod main ++StartParams --quiet --StopClass org.elasticsearch.bootstrap.Elasticsearch --StopMethod close --Classpath "%ES_CLASSPATH%" --JvmMs %JVM_MS% --JvmMx %JVM_MX% --JvmSs %JVM_SS% --JvmOptions %ES_JAVA_OPTS% ++JvmOptions %ES_PARAMS% %LOG_OPTS% --PidFile "%SERVICE_ID%.pid" --DisplayName "%SERVICE_DISPLAY_NAME%" --Description "%SERVICE_DESCRIPTION%" --Jvm "%%JAVA_HOME%%%JVM_DLL%" --StartMode jvm --StopMode jvm --StartPath "%ES_HOME%" %SERVICE_PARAMS%
"%EXECUTABLE%" //IS//%SERVICE_ID% --Startup %ES_START_TYPE% --StopTimeout %ES_STOP_TIMEOUT% --StartClass org.elasticsearch.bootstrap.Elasticsearch --StartMethod main ++StartParams --quiet --StopClass org.elasticsearch.bootstrap.Elasticsearch --StopMethod close --Classpath "%ES_CLASSPATH%" --JvmMs %JVM_MS% --JvmMx %JVM_MX% --JvmSs %JVM_SS% --JvmOptions %ES_JAVA_OPTS% ++JvmOptions %ES_PARAMS% %LOG_OPTS% --PidFile "%SERVICE_ID%.pid" --DisplayName "%SERVICE_DISPLAY_NAME%" --Description "%SERVICE_DESCRIPTION%" --Jvm "%%JAVA_HOME%%%JVM_DLL%" --StartMode jvm --StopMode jvm --StartPath "%ES_HOME%" %SERVICE_PARAMS% ++Environment HOSTNAME="%%COMPUTERNAME%%"
if not errorlevel 1 goto installed
echo Failed installing '%SERVICE_ID%' service

View File

@ -0,0 +1,71 @@
[[java-rest-high-x-pack-rollup-get-job]]
=== Get Rollup Job API
The Get Rollup Job API can be used to get one or all rollup jobs from the
cluster. It accepts a `GetRollupJobRequest` object as a request and returns
a `GetRollupJobResponse`.
[[java-rest-high-x-pack-rollup-get-rollup-job-request]]
==== Get Rollup Job Request
A `GetRollupJobRequest` can be built without any parameters to get all of the
rollup jobs or with a job name to get a single job:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/RollupDocumentationIT.java[x-pack-rollup-get-rollup-job-request]
--------------------------------------------------
<1> Gets all jobs.
<2> Gets `job_1`.
[[java-rest-high-x-pack-rollup-get-rollup-job-execution]]
==== Execution
The Get Rollup Job API can be executed through a `RollupClient`
instance. Such instance can be retrieved from a `RestHighLevelClient`
using the `rollup()` method:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/RollupDocumentationIT.java[x-pack-rollup-get-rollup-job-execute]
--------------------------------------------------
[[java-rest-high-x-pack-rollup-get-rollup-job-response]]
==== Response
The returned `GetRollupJobResponse` includes a `JobWrapper` per returned job
which contains the configuration of the job, the job's current status, and
statistics about the job's past execution.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/RollupDocumentationIT.java[x-pack-rollup-get-rollup-job-response]
--------------------------------------------------
<1> We only asked for a single job
[[java-rest-high-x-pack-rollup-get-rollup-job-async]]
==== Asynchronous Execution
This request can be executed asynchronously:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/RollupDocumentationIT.java[x-pack-rollup-get-rollup-job-execute-async]
--------------------------------------------------
<1> The `GetRollupJobRequest` to execute and the `ActionListener` to use when
the execution completes
The asynchronous method does not block and returns immediately. Once it is
completed the `ActionListener` is called back using the `onResponse` method
if the execution successfully completed or using the `onFailure` method if
it failed.
A typical listener for `GetRollupJobResponse` looks like:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/RollupDocumentationIT.java[x-pack-rollup-get-rollup-job-execute-listener]
--------------------------------------------------
<1> Called when the execution is successfully completed. The response is
provided as an argument
<2> Called in case of failure. The raised exception is provided as an argument

View File

@ -0,0 +1,46 @@
[[java-rest-high-security-change-password]]
=== Change Password API
[[java-rest-high-security-change-password-execution]]
==== Execution
A user's password can be changed using the `security().changePassword()`
method:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SecurityDocumentationIT.java[change-password-execute]
--------------------------------------------------
[[java-rest-high-change-password-response]]
==== Response
The returned `EmptyResponse` does not contain any fields. The return of this
response indicates a successful request.
[[java-rest-high-x-pack-security-change-password-async]]
==== Asynchronous Execution
This request can be executed asynchronously:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SecurityDocumentationIT.java[change-password-execute-async]
--------------------------------------------------
<1> The `ChangePassword` request to execute and the `ActionListener` to use when
the execution completes.
The asynchronous method does not block and returns immediately. Once the request
has completed the `ActionListener` is called back using the `onResponse` method
if the execution successfully completed or using the `onFailure` method if
it failed.
A typical listener for a `EmptyResponse` looks like:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SecurityDocumentationIT.java[change-password-execute-listener]
--------------------------------------------------
<1> Called when the execution is successfully completed. The response is
provided as an argument.
<2> Called in case of failure. The raised exception is provided as an argument.

View File

@ -285,8 +285,10 @@ include::migration/get-assistance.asciidoc[]
The Java High Level REST Client supports the following Rollup APIs:
* <<java-rest-high-x-pack-rollup-put-job>>
* <<java-rest-high-x-pack-rollup-get-job>>
include::rollup/put_job.asciidoc[]
include::rollup/get_job.asciidoc[]
== Security APIs
@ -295,10 +297,12 @@ The Java High Level REST Client supports the following Security APIs:
* <<java-rest-high-security-put-user>>
* <<java-rest-high-security-enable-user>>
* <<java-rest-high-security-disable-user>>
* <<java-rest-high-security-change-password>>
include::security/put-user.asciidoc[]
include::security/enable-user.asciidoc[]
include::security/disable-user.asciidoc[]
include::security/change-password.asciidoc[]
== Watcher APIs

View File

@ -56,8 +56,8 @@ releases 2.0 and later do not support rivers.
* https://github.com/jprante/elasticsearch-jdbc[JDBC importer]:
The Java Database Connection (JDBC) importer allows to fetch data from JDBC sources for indexing into Elasticsearch (by Jörg Prante)
* https://github.com/reachkrishnaraj/kafka-elasticsearch-standalone-consumer/tree/branch2.0[Kafka Standalone Consumer(Indexer)]:
Kafka Standalone Consumer [Indexer] will read messages from Kafka in batches, processes(as implemented) and bulk-indexes them into Elasticsearch. Flexible and scalable. More documentation in above GitHub repo's Wiki.(Please use branch 2.0)!
* https://github.com/BigDataDevs/kafka-elasticsearch-consumer [Kafka Standalone Consumer(Indexer)]:
Kafka Standalone Consumer [Indexer] will read messages from Kafka in batches, processes(as implemented) and bulk-indexes them into Elasticsearch. Flexible and scalable. More documentation in above GitHub repo's Wiki.
* https://github.com/ozlerhakan/mongolastic[Mongolastic]:
A tool that clones data from Elasticsearch to MongoDB and vice versa

View File

@ -615,7 +615,7 @@ GET /_search
"aggs" : {
"genres" : {
"terms" : {
"field" : "gender",
"field" : "genre",
"script" : {
"source" : "'Genre: ' +_value",
"lang" : "painless"

View File

@ -16,6 +16,7 @@ GET _tasks?nodes=nodeId1,nodeId2 <2>
GET _tasks?nodes=nodeId1,nodeId2&actions=cluster:* <3>
--------------------------------------------------
// CONSOLE
// TEST[skip:No tasks to retrieve]
<1> Retrieves all tasks currently running on all nodes in the cluster.
<2> Retrieves all tasks running on nodes `nodeId1` and `nodeId2`. See <<cluster-nodes>> for more info about how to select individual nodes.
@ -57,31 +58,29 @@ The result will look similar to the following:
}
}
--------------------------------------------------
// NOTCONSOLE
// We can't test tasks output
// TESTRESPONSE
It is also possible to retrieve information for a particular task:
It is also possible to retrieve information for a particular task. The following
example retrieves information about task `oTUltX4IQMOUUVeiohTt8A:124`:
[source,js]
--------------------------------------------------
GET _tasks/task_id <1>
GET _tasks/oTUltX4IQMOUUVeiohTt8A:124
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
// TEST[catch:missing]
<1> This will return a 404 if the task isn't found.
If the task isn't found, the API returns a 404.
Or to retrieve all children of a particular task:
To retrieve all children of a particular task:
[source,js]
--------------------------------------------------
GET _tasks?parent_task_id=parent_task_id <1>
GET _tasks?parent_task_id=oTUltX4IQMOUUVeiohTt8A:123
--------------------------------------------------
// CONSOLE
// TEST[s/=parent_task_id/=node_id:1/]
<1> This won't return a 404 if the parent isn't found.
If the parent isn't found, the API does not return a 404.
You can also use the `detailed` request parameter to get more information about
the running tasks. This is useful for telling one task from another but is more
@ -93,8 +92,9 @@ request parameter:
GET _tasks?actions=*search&detailed
--------------------------------------------------
// CONSOLE
// TEST[skip:No tasks to retrieve]
might look like:
The results might look like:
[source,js]
--------------------------------------------------
@ -121,8 +121,7 @@ might look like:
}
}
--------------------------------------------------
// NOTCONSOLE
// We can't test tasks output
// TESTRESPONSE
The new `description` field contains human readable text that identifies the
particular request that the task is performing such as identifying the search
@ -167,14 +166,14 @@ GET _cat/tasks?detailed
[[task-cancellation]]
=== Task Cancellation
If a long-running task supports cancellation, it can be cancelled by the following command:
If a long-running task supports cancellation, it can be cancelled with the cancel
tasks API. The following example cancels task `oTUltX4IQMOUUVeiohTt8A:12345`:
[source,js]
--------------------------------------------------
POST _tasks/node_id:task_id/_cancel
POST _tasks/oTUltX4IQMOUUVeiohTt8A:12345/_cancel
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/1/]
The task cancellation command supports the same task selection parameters as the list tasks command, so multiple tasks
can be cancelled at the same time. For example, the following command will cancel all reindex tasks running on the
@ -217,7 +216,7 @@ a the client that started them:
--------------------------------------------------
curl -i -H "X-Opaque-Id: 123456" "http://localhost:9200/_tasks?group_by=parents"
--------------------------------------------------
// NOTCONSOLE
//NOTCONSOLE
The result will look similar to the following:
@ -260,8 +259,7 @@ content-length: 831
}
}
--------------------------------------------------
// NOTCONSOLE
//NOTCONSOLE
<1> id as a part of the response header
<2> id for the tasks that was initiated by the REST request
<3> the child task of the task initiated by the REST request

View File

@ -304,6 +304,7 @@ You can fetch the status of any running delete-by-query requests with the
GET _tasks?detailed=true&actions=*/delete/byquery
--------------------------------------------------
// CONSOLE
// TEST[skip:No tasks to retrieve]
The responses looks like:
@ -344,9 +345,7 @@ The responses looks like:
}
}
--------------------------------------------------
// NOTCONSOLE
// We can't test tasks output
// TESTRESPONSE
<1> this object contains the actual status. It is just like the response json
with the important addition of the `total` field. `total` is the total number
of operations that the reindex expects to perform. You can estimate the
@ -357,10 +356,9 @@ With the task id you can look up the task directly:
[source,js]
--------------------------------------------------
GET /_tasks/task_id
GET /_tasks/r1A2WoRbTwKZ516z6NEs5A:36619
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
// TEST[catch:missing]
The advantage of this API is that it integrates with `wait_for_completion=false`
@ -375,16 +373,15 @@ you to delete that document.
[[docs-delete-by-query-cancel-task-api]]
=== Works with the Cancel Task API
Any Delete By Query can be canceled using the <<tasks,Task Cancel API>>:
Any Delete By Query can be canceled using the <<tasks,task cancel API>>:
[source,js]
--------------------------------------------------
POST _tasks/task_id/_cancel
POST _tasks/r1A2WoRbTwKZ516z6NEs5A:36619/_cancel
--------------------------------------------------
// TEST[s/task_id/node_id:1/]
// CONSOLE
The `task_id` can be found using the tasks API above.
The task ID can be found using the <<tasks,tasks API>>.
Cancellation should happen quickly but might take a few seconds. The task status
API above will continue to list the task until it is wakes to cancel itself.
@ -399,12 +396,11 @@ using the `_rethrottle` API:
[source,js]
--------------------------------------------------
POST _delete_by_query/task_id/_rethrottle?requests_per_second=-1
POST _delete_by_query/r1A2WoRbTwKZ516z6NEs5A:36619/_rethrottle?requests_per_second=-1
--------------------------------------------------
// TEST[s/task_id/node_id:1/]
// CONSOLE
The `task_id` can be found using the tasks API above.
The task ID can be found using the <<tasks,tasks API>>.
Just like when setting it on the `_delete_by_query` API `requests_per_second`
can be either `-1` to disable throttling or any decimal number

View File

@ -692,6 +692,7 @@ You can fetch the status of all running reindex requests with the
GET _tasks?detailed=true&actions=*reindex
--------------------------------------------------
// CONSOLE
// TEST[skip:No tasks to retrieve]
The response looks like:
@ -726,32 +727,36 @@ The response looks like:
"bulk": 0,
"search": 0
},
"throttled_millis": 0
"throttled_millis": 0,
"requests_per_second": -1,
"throttled_until_millis": 0
},
"description" : ""
"description" : "",
"start_time_in_millis": 1535149899665,
"running_time_in_nanos": 5926916792,
"cancellable": true,
"headers": {}
}
}
}
}
}
--------------------------------------------------
// NOTCONSOLE
// We can't test tasks output
// TESTRESPONSE
<1> this object contains the actual status. It is identical to the response JSON
except for the important addition of the `total` field. `total` is the total number
of operations that the `_reindex` expects to perform. You can estimate the
progress by adding the `updated`, `created`, and `deleted` fields. The request
will finish when their sum is equal to the `total` field.
With the task id you can look up the task directly:
With the task id you can look up the task directly. The following example
retrieves information about the task `r1A2WoRbTwKZ516z6NEs5A:36619`:
[source,js]
--------------------------------------------------
GET /_tasks/task_id
GET /_tasks/r1A2WoRbTwKZ516z6NEs5A:36619
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
// TEST[catch:missing]
The advantage of this API is that it integrates with `wait_for_completion=false`
@ -766,16 +771,16 @@ you to delete that document.
[[docs-reindex-cancel-task-api]]
=== Works with the Cancel Task API
Any Reindex can be canceled using the <<tasks,Task Cancel API>>:
Any Reindex can be canceled using the <<task-cancellation,Task Cancel API>>. For
example:
[source,js]
--------------------------------------------------
POST _tasks/task_id/_cancel
POST _tasks/r1A2WoRbTwKZ516z6NEs5A:36619/_cancel
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
The `task_id` can be found using the Tasks API.
The task ID can be found using the <<tasks,Tasks API>>.
Cancelation should happen quickly but might take a few seconds. The Tasks
API will continue to list the task until it wakes to cancel itself.
@ -790,12 +795,11 @@ the `_rethrottle` API:
[source,js]
--------------------------------------------------
POST _reindex/task_id/_rethrottle?requests_per_second=-1
POST _reindex/r1A2WoRbTwKZ516z6NEs5A:36619/_rethrottle?requests_per_second=-1
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
The `task_id` can be found using the Tasks API above.
The task ID can be found using the <<tasks,tasks API>>.
Just like when setting it on the Reindex API, `requests_per_second`
can be either `-1` to disable throttling or any decimal number

View File

@ -359,6 +359,7 @@ You can fetch the status of all running update-by-query requests with the
GET _tasks?detailed=true&actions=*byquery
--------------------------------------------------
// CONSOLE
// TEST[skip:No tasks to retrieve]
The responses looks like:
@ -392,7 +393,7 @@ The responses looks like:
"retries": {
"bulk": 0,
"search": 0
}
},
"throttled_millis": 0
},
"description" : ""
@ -402,8 +403,7 @@ The responses looks like:
}
}
--------------------------------------------------
// NOTCONSOLE
// We can't test tasks output
// TESTRESPONSE
<1> this object contains the actual status. It is just like the response json
with the important addition of the `total` field. `total` is the total number
@ -411,14 +411,14 @@ of operations that the reindex expects to perform. You can estimate the
progress by adding the `updated`, `created`, and `deleted` fields. The request
will finish when their sum is equal to the `total` field.
With the task id you can look up the task directly:
With the task id you can look up the task directly. The following example
retrieves information about task `r1A2WoRbTwKZ516z6NEs5A:36619`:
[source,js]
--------------------------------------------------
GET /_tasks/task_id
GET /_tasks/r1A2WoRbTwKZ516z6NEs5A:36619
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
// TEST[catch:missing]
The advantage of this API is that it integrates with `wait_for_completion=false`
@ -437,12 +437,11 @@ Any Update By Query can be canceled using the <<tasks,Task Cancel API>>:
[source,js]
--------------------------------------------------
POST _tasks/task_id/_cancel
POST _tasks/r1A2WoRbTwKZ516z6NEs5A:36619/_cancel
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
The `task_id` can be found using the tasks API above.
The task ID can be found using the <<tasks,tasks API>>.
Cancellation should happen quickly but might take a few seconds. The task status
API above will continue to list the task until it is wakes to cancel itself.
@ -457,12 +456,11 @@ using the `_rethrottle` API:
[source,js]
--------------------------------------------------
POST _update_by_query/task_id/_rethrottle?requests_per_second=-1
POST _update_by_query/r1A2WoRbTwKZ516z6NEs5A:36619/_rethrottle?requests_per_second=-1
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
The `task_id` can be found using the tasks API above.
The task ID can be found using the <<tasks, tasks API>>.
Just like when setting it on the `_update_by_query` API `requests_per_second`
can be either `-1` to disable throttling or any decimal number

View File

@ -43,6 +43,12 @@ PUT /_cluster/settings
-------------------------------
// CONSOLE
IMPORTANT: User-defined cluster metadata is not intended to store sensitive or
confidential information. Any information stored in user-defined cluster
metadata will be viewable by anyone with access to the
<<cluster-get-settings,Cluster Get Settings>> API, and is recorded in the
{es} logs.
[[cluster-max-tombstones]]
==== Index Tombstones

View File

@ -22,6 +22,17 @@ it excludes (due to `-`) all indices that start with `l`.
This notation is very convenient and powerful as it allows both inclusion and exclusion, depending on
the target naming convention.
The same kind of patterns can also be used to query multiple indices or tables.
For example:
["source","sql",subs="attributes,callouts,macros"]
----
include-tagged::{sql-specs}/docs.csv-spec[fromTablePatternQuoted]
----
NOTE: There is the restriction that all resolved concrete tables have the exact same mapping.
* SQL `LIKE` notation
The common `LIKE` statement (including escaping if needed) to match a wildcard pattern, based on one `_`

View File

@ -88,7 +88,7 @@ where:
Represents the name (optionally qualified) of an existing table, either a concrete or base one (actual index) or alias.
If the table name contains special SQL characters (such as `.`,`-`,etc...) use double quotes to escape them:
If the table name contains special SQL characters (such as `.`,`-`,`*`,etc...) use double quotes to escape them:
["source","sql",subs="attributes,callouts,macros"]
----

View File

@ -33,7 +33,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.stream.Collectors;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
@ -119,8 +119,8 @@ public class DiscountedCumulativeGain implements EvaluationMetric {
@Override
public Optional<Integer> forcedSearchSize() {
return Optional.of(k);
public OptionalInt forcedSearchSize() {
return OptionalInt.of(k);
}
@Override
@ -130,9 +130,13 @@ public class DiscountedCumulativeGain implements EvaluationMetric {
List<Integer> ratingsInSearchHits = new ArrayList<>(ratedHits.size());
int unratedResults = 0;
for (RatedSearchHit hit : ratedHits) {
// unknownDocRating might be null, in which case unrated docs will be ignored in the dcg calculation.
// we still need to add them as a placeholder so the rank of the subsequent ratings is correct
ratingsInSearchHits.add(hit.getRating().orElse(unknownDocRating));
if (hit.getRating().isPresent()) {
ratingsInSearchHits.add(hit.getRating().getAsInt());
} else {
// unknownDocRating might be null, in which case unrated docs will be ignored in the dcg calculation.
// we still need to add them as a placeholder so the rank of the subsequent ratings is correct
ratingsInSearchHits.add(unknownDocRating);
}
if (hit.getRating().isPresent() == false) {
unratedResults++;
}

View File

@ -29,7 +29,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.stream.Collectors;
/**
@ -64,9 +64,9 @@ public interface EvaluationMetric extends ToXContentObject, NamedWriteable {
DocumentKey key = new DocumentKey(hit.getIndex(), hit.getId());
RatedDocument ratedDoc = ratedDocumentMap.get(key);
if (ratedDoc != null) {
ratedSearchHits.add(new RatedSearchHit(hit, Optional.of(ratedDoc.getRating())));
ratedSearchHits.add(new RatedSearchHit(hit, OptionalInt.of(ratedDoc.getRating())));
} else {
ratedSearchHits.add(new RatedSearchHit(hit, Optional.empty()));
ratedSearchHits.add(new RatedSearchHit(hit, OptionalInt.empty()));
}
}
return ratedSearchHits;
@ -93,7 +93,7 @@ public interface EvaluationMetric extends ToXContentObject, NamedWriteable {
* this method. The default implementation returns an empty optional.
* @return the number of search hits this metrics requests
*/
default Optional<Integer> forcedSearchSize() {
return Optional.empty();
default OptionalInt forcedSearchSize() {
return OptionalInt.empty();
}
}

View File

@ -32,7 +32,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
@ -126,8 +126,8 @@ public class ExpectedReciprocalRank implements EvaluationMetric {
@Override
public Optional<Integer> forcedSearchSize() {
return Optional.of(k);
public OptionalInt forcedSearchSize() {
return OptionalInt.of(k);
}
@Override
@ -139,9 +139,13 @@ public class ExpectedReciprocalRank implements EvaluationMetric {
List<Integer> ratingsInSearchHits = new ArrayList<>(ratedHits.size());
int unratedResults = 0;
for (RatedSearchHit hit : ratedHits) {
// unknownDocRating might be null, in which case unrated will be ignored in the calculation.
// we still need to add them as a placeholder so the rank of the subsequent ratings is correct
ratingsInSearchHits.add(hit.getRating().orElse(unknownDocRating));
if (hit.getRating().isPresent()) {
ratingsInSearchHits.add(hit.getRating().getAsInt());
} else {
// unknownDocRating might be null, in which case unrated docs will be ignored in the dcg calculation.
// we still need to add them as a placeholder so the rank of the subsequent ratings is correct
ratingsInSearchHits.add(unknownDocRating);
}
if (hit.getRating().isPresent() == false) {
unratedResults++;
}

View File

@ -30,7 +30,7 @@ import org.elasticsearch.search.SearchHit;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
@ -90,8 +90,8 @@ public class MeanReciprocalRank implements EvaluationMetric {
}
@Override
public Optional<Integer> forcedSearchSize() {
return Optional.of(k);
public OptionalInt forcedSearchSize() {
return OptionalInt.of(k);
}
@Override
@ -115,9 +115,9 @@ public class MeanReciprocalRank implements EvaluationMetric {
int firstRelevant = -1;
int rank = 1;
for (RatedSearchHit hit : ratedHits) {
Optional<Integer> rating = hit.getRating();
OptionalInt rating = hit.getRating();
if (rating.isPresent()) {
if (rating.get() >= this.relevantRatingThreshhold) {
if (rating.getAsInt() >= this.relevantRatingThreshhold) {
firstRelevant = rank;
break;
}

View File

@ -30,7 +30,7 @@ import org.elasticsearch.search.SearchHit;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import javax.naming.directory.SearchResult;
@ -144,8 +144,8 @@ public class PrecisionAtK implements EvaluationMetric {
}
@Override
public Optional<Integer> forcedSearchSize() {
return Optional.of(k);
public OptionalInt forcedSearchSize() {
return OptionalInt.of(k);
}
public static PrecisionAtK fromXContent(XContentParser parser) {
@ -164,9 +164,9 @@ public class PrecisionAtK implements EvaluationMetric {
int falsePositives = 0;
List<RatedSearchHit> ratedSearchHits = joinHitsWithRatings(hits, ratedDocs);
for (RatedSearchHit hit : ratedSearchHits) {
Optional<Integer> rating = hit.getRating();
OptionalInt rating = hit.getRating();
if (rating.isPresent()) {
if (rating.get() >= this.relevantRatingThreshhold) {
if (rating.getAsInt() >= this.relevantRatingThreshhold) {
truePositives++;
} else {
falsePositives++;

View File

@ -33,7 +33,7 @@ import org.elasticsearch.search.SearchHit;
import java.io.IOException;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
/**
* Combines a {@link SearchHit} with a document rating.
@ -41,16 +41,16 @@ import java.util.Optional;
public class RatedSearchHit implements Writeable, ToXContentObject {
private final SearchHit searchHit;
private final Optional<Integer> rating;
private final OptionalInt rating;
public RatedSearchHit(SearchHit searchHit, Optional<Integer> rating) {
public RatedSearchHit(SearchHit searchHit, OptionalInt rating) {
this.searchHit = searchHit;
this.rating = rating;
}
RatedSearchHit(StreamInput in) throws IOException {
this(SearchHit.readSearchHit(in),
in.readBoolean() == true ? Optional.of(in.readVInt()) : Optional.empty());
in.readBoolean() == true ? OptionalInt.of(in.readVInt()) : OptionalInt.empty());
}
@Override
@ -58,7 +58,7 @@ public class RatedSearchHit implements Writeable, ToXContentObject {
searchHit.writeTo(out);
out.writeBoolean(rating.isPresent());
if (rating.isPresent()) {
out.writeVInt(rating.get());
out.writeVInt(rating.getAsInt());
}
}
@ -66,7 +66,7 @@ public class RatedSearchHit implements Writeable, ToXContentObject {
return this.searchHit;
}
public Optional<Integer> getRating() {
public OptionalInt getRating() {
return this.rating;
}
@ -75,22 +75,21 @@ public class RatedSearchHit implements Writeable, ToXContentObject {
throws IOException {
builder.startObject();
builder.field("hit", (ToXContent) searchHit);
builder.field("rating", rating.orElse(null));
builder.field("rating", rating.isPresent() ? rating.getAsInt() : null);
builder.endObject();
return builder;
}
private static final ParseField HIT_FIELD = new ParseField("hit");
private static final ParseField RATING_FIELD = new ParseField("rating");
@SuppressWarnings("unchecked")
private static final ConstructingObjectParser<RatedSearchHit, Void> PARSER = new ConstructingObjectParser<>("rated_hit", true,
a -> new RatedSearchHit((SearchHit) a[0], (Optional<Integer>) a[1]));
a -> new RatedSearchHit((SearchHit) a[0], (OptionalInt) a[1]));
static {
PARSER.declareObject(ConstructingObjectParser.constructorArg(), (p, c) -> SearchHit.fromXContent(p), HIT_FIELD);
PARSER.declareField(ConstructingObjectParser.constructorArg(),
(p) -> p.currentToken() == XContentParser.Token.VALUE_NULL ? Optional.empty() : Optional.of(p.intValue()), RATING_FIELD,
ValueType.INT_OR_NULL);
(p) -> p.currentToken() == XContentParser.Token.VALUE_NULL ? OptionalInt.empty() : OptionalInt.of(p.intValue()),
RATING_FIELD, ValueType.INT_OR_NULL);
}
public static RatedSearchHit parse(XContentParser parser) throws IOException {

View File

@ -119,7 +119,7 @@ public class TransportRankEvalAction extends HandledTransportAction<RankEvalRequ
}
if (metric.forcedSearchSize().isPresent()) {
evaluationRequest.size(metric.forcedSearchSize().get());
evaluationRequest.size(metric.forcedSearchSize().getAsInt());
}
ratedRequestsInSearch.add(ratedRequest);

View File

@ -223,7 +223,8 @@ public class PrecisionAtKTests extends ESTestCase {
}
private static PrecisionAtK copy(PrecisionAtK original) {
return new PrecisionAtK(original.getRelevantRatingThreshold(), original.getIgnoreUnlabeled(), original.forcedSearchSize().get());
return new PrecisionAtK(original.getRelevantRatingThreshold(), original.getIgnoreUnlabeled(),
original.forcedSearchSize().getAsInt());
}
private static PrecisionAtK mutate(PrecisionAtK original) {
@ -231,15 +232,15 @@ public class PrecisionAtKTests extends ESTestCase {
switch (randomIntBetween(0, 2)) {
case 0:
pAtK = new PrecisionAtK(original.getRelevantRatingThreshold(), !original.getIgnoreUnlabeled(),
original.forcedSearchSize().get());
original.forcedSearchSize().getAsInt());
break;
case 1:
pAtK = new PrecisionAtK(randomValueOtherThan(original.getRelevantRatingThreshold(), () -> randomIntBetween(0, 10)),
original.getIgnoreUnlabeled(), original.forcedSearchSize().get());
original.getIgnoreUnlabeled(), original.forcedSearchSize().getAsInt());
break;
case 2:
pAtK = new PrecisionAtK(original.getRelevantRatingThreshold(),
original.getIgnoreUnlabeled(), original.forcedSearchSize().get() + 1);
original.getIgnoreUnlabeled(), original.forcedSearchSize().getAsInt() + 1);
break;
default:
throw new IllegalStateException("The test should only allow three parameters mutated");

View File

@ -128,7 +128,7 @@ public class RankEvalRequestIT extends ESIntegTestCase {
if (id.equals("1") || id.equals("6")) {
assertFalse(hit.getRating().isPresent());
} else {
assertEquals(RELEVANT_RATING_1, hit.getRating().get().intValue());
assertEquals(RELEVANT_RATING_1, hit.getRating().getAsInt());
}
}
}
@ -139,7 +139,7 @@ public class RankEvalRequestIT extends ESIntegTestCase {
for (RatedSearchHit hit : hitsAndRatings) {
String id = hit.getSearchHit().getId();
if (id.equals("1")) {
assertEquals(RELEVANT_RATING_1, hit.getRating().get().intValue());
assertEquals(RELEVANT_RATING_1, hit.getRating().getAsInt());
} else {
assertFalse(hit.getRating().isPresent());
}

View File

@ -50,7 +50,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.function.Predicate;
import static java.util.Collections.singleton;
@ -182,6 +182,6 @@ public class RankEvalResponseTests extends ESTestCase {
SearchHit hit = new SearchHit(docId, docId + "", new Text(""), Collections.emptyMap());
hit.shard(new SearchShardTarget("testnode", new Index(index, "uuid"), 0, null));
hit.score(1.0f);
return new RatedSearchHit(hit, rating != null ? Optional.of(rating) : Optional.empty());
return new RatedSearchHit(hit, rating != null ? OptionalInt.of(rating) : OptionalInt.empty());
}
}

View File

@ -30,7 +30,7 @@ import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.Collections;
import java.util.Optional;
import java.util.OptionalInt;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.elasticsearch.test.XContentTestUtils.insertRandomFields;
@ -38,8 +38,8 @@ import static org.elasticsearch.test.XContentTestUtils.insertRandomFields;
public class RatedSearchHitTests extends ESTestCase {
public static RatedSearchHit randomRatedSearchHit() {
Optional<Integer> rating = randomBoolean() ? Optional.empty()
: Optional.of(randomIntBetween(0, 5));
OptionalInt rating = randomBoolean() ? OptionalInt.empty()
: OptionalInt.of(randomIntBetween(0, 5));
SearchHit searchHit = new SearchHit(randomIntBetween(0, 10), randomAlphaOfLength(10),
new Text(randomAlphaOfLength(10)), Collections.emptyMap());
RatedSearchHit ratedSearchHit = new RatedSearchHit(searchHit, rating);
@ -47,11 +47,11 @@ public class RatedSearchHitTests extends ESTestCase {
}
private static RatedSearchHit mutateTestItem(RatedSearchHit original) {
Optional<Integer> rating = original.getRating();
OptionalInt rating = original.getRating();
SearchHit hit = original.getSearchHit();
switch (randomIntBetween(0, 1)) {
case 0:
rating = rating.isPresent() ? Optional.of(rating.get() + 1) : Optional.of(randomInt(5));
rating = rating.isPresent() ? OptionalInt.of(rating.getAsInt() + 1) : OptionalInt.of(randomInt(5));
break;
case 1:
hit = new SearchHit(hit.docId(), hit.getId() + randomAlphaOfLength(10),

View File

@ -31,8 +31,10 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.plugins.ScriptPlugin;
import org.elasticsearch.script.ScoreScript;
import org.elasticsearch.script.ScoreScript.LeafFactory;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptEngine;
import org.elasticsearch.search.lookup.SearchLookup;
/**
* An example script plugin that adds a {@link ScriptEngine} implementing expert scoring.
@ -53,81 +55,106 @@ public class ExpertScriptPlugin extends Plugin implements ScriptPlugin {
}
@Override
public <T> T compile(String scriptName, String scriptSource, ScriptContext<T> context, Map<String, String> params) {
public <T> T compile(String scriptName, String scriptSource,
ScriptContext<T> context, Map<String, String> params) {
if (context.equals(ScoreScript.CONTEXT) == false) {
throw new IllegalArgumentException(getType() + " scripts cannot be used for context [" + context.name + "]");
throw new IllegalArgumentException(getType()
+ " scripts cannot be used for context ["
+ context.name + "]");
}
// we use the script "source" as the script identifier
if ("pure_df".equals(scriptSource)) {
ScoreScript.Factory factory = (p, lookup) -> new ScoreScript.LeafFactory() {
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 ScoreScript newInstance(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 new ScoreScript(p, lookup, context) {
@Override
public double execute() {
return 0.0d;
}
};
}
return new ScoreScript(p, lookup, context) {
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 execute() {
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 needs_score() {
return false;
}
};
ScoreScript.Factory factory = PureDfLeafFactory::new;
return context.factoryClazz.cast(factory);
}
throw new IllegalArgumentException("Unknown script name " + scriptSource);
throw new IllegalArgumentException("Unknown script name "
+ scriptSource);
}
@Override
public void close() {
// optionally close resources
}
private static class PureDfLeafFactory implements LeafFactory {
private final Map<String, Object> params;
private final SearchLookup lookup;
private final String field;
private final String term;
private PureDfLeafFactory(
Map<String, Object> params, SearchLookup lookup) {
if (params.containsKey("field") == false) {
throw new IllegalArgumentException(
"Missing parameter [field]");
}
if (params.containsKey("term") == false) {
throw new IllegalArgumentException(
"Missing parameter [term]");
}
this.params = params;
this.lookup = lookup;
field = params.get("field").toString();
term = params.get("term").toString();
}
@Override
public boolean needs_score() {
return false; // Return true if the script needs the score
}
@Override
public ScoreScript newInstance(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 new ScoreScript(params, lookup, context) {
@Override
public double execute() {
return 0.0d;
}
};
}
return new ScoreScript(params, lookup, context) {
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 execute() {
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);
}
}
};
}
}
}
// end::expert_engine
}

View File

@ -148,12 +148,12 @@ public class RecoveryIT extends AbstractRollingTestCase {
break;
case UPGRADED:
updateIndexSettings(index, Settings.builder().put(INDEX_ROUTING_ALLOCATION_ENABLE_SETTING.getKey(), (String)null));
asyncIndexDocs(index, 60, 50).get();
asyncIndexDocs(index, 60, 45).get();
ensureGreen(index);
client().performRequest(new Request("POST", index + "/_refresh"));
assertCount(index, "_only_nodes:" + nodes.get(0), 110);
assertCount(index, "_only_nodes:" + nodes.get(1), 110);
assertCount(index, "_only_nodes:" + nodes.get(2), 110);
assertCount(index, "_only_nodes:" + nodes.get(0), 105);
assertCount(index, "_only_nodes:" + nodes.get(1), 105);
assertCount(index, "_only_nodes:" + nodes.get(2), 105);
break;
default:
throw new IllegalStateException("unknown type " + CLUSTER_TYPE);
@ -165,7 +165,7 @@ public class RecoveryIT extends AbstractRollingTestCase {
request.addParameter("preference", preference);
final Response response = client().performRequest(request);
final int actualCount = Integer.parseInt(ObjectPath.createFromResponse(response).evaluate("count").toString());
assertThat(actualCount, equalTo(expectedCount));
assertThat("preference [" + preference + "]", actualCount, equalTo(expectedCount));
}
@ -225,7 +225,7 @@ public class RecoveryIT extends AbstractRollingTestCase {
.put(IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 2)
.put("index.routing.allocation.include._id", (String)null)
);
asyncIndexDocs(index, 60, 50).get();
asyncIndexDocs(index, 60, 45).get();
ensureGreen(index);
client().performRequest(new Request("POST", index + "/_refresh"));
Response response = client().performRequest(new Request("GET", "_nodes"));
@ -233,9 +233,9 @@ public class RecoveryIT extends AbstractRollingTestCase {
final Map<String, Object> nodeMap = objectPath.evaluate("nodes");
List<String> nodes = new ArrayList<>(nodeMap.keySet());
assertCount(index, "_only_nodes:" + nodes.get(0), 110);
assertCount(index, "_only_nodes:" + nodes.get(1), 110);
assertCount(index, "_only_nodes:" + nodes.get(2), 110);
assertCount(index, "_only_nodes:" + nodes.get(0), 105);
assertCount(index, "_only_nodes:" + nodes.get(1), 105);
assertCount(index, "_only_nodes:" + nodes.get(2), 105);
break;
default:
throw new IllegalStateException("unknown type " + CLUSTER_TYPE);

View File

@ -0,0 +1,299 @@
---
"Search by suggestion and by keyword sub-field should work":
- skip:
version: " - 6.99.99"
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
- do:
indices.create:
index: completion_with_sub_keyword
body:
mappings:
test:
"properties":
"suggest_1":
"type" : "completion"
"fields":
"text_raw":
"type" : "keyword"
- do:
index:
index: completion_with_sub_keyword
type: test
id: 1
body:
suggest_1: "bar"
- do:
index:
index: completion_with_sub_keyword
type: test
id: 2
body:
suggest_1: "baz"
- do:
indices.refresh: {}
- do:
search:
index: completion_with_sub_keyword
body:
suggest:
result:
text: "b"
completion:
field: suggest_1
- length: { suggest.result: 1 }
- length: { suggest.result.0.options: 2 }
- do:
search:
index: completion_with_sub_keyword
body:
query: { term: { suggest_1.text_raw: "bar" }}
- match: { hits.total: 1 }
---
"Search by suggestion on sub field should work":
- skip:
version: " - 6.99.99"
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
- do:
indices.create:
index: completion_with_sub_completion
body:
mappings:
test:
"properties":
"suggest_1":
"type": "completion"
"fields":
"suggest_2":
"type": "completion"
- do:
index:
index: completion_with_sub_completion
type: test
id: 1
body:
suggest_1: "bar"
- do:
index:
index: completion_with_sub_completion
type: test
id: 2
body:
suggest_1: "baz"
- do:
indices.refresh: {}
- do:
search:
index: completion_with_sub_completion
body:
suggest:
result:
text: "b"
completion:
field: suggest_1.suggest_2
- length: { suggest.result: 1 }
- length: { suggest.result.0.options: 2 }
---
"Search by suggestion on sub field with context should work":
- skip:
version: " - 6.99.99"
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
- do:
indices.create:
index: completion_with_context
body:
mappings:
test:
"properties":
"suggest_1":
"type": "completion"
"contexts":
-
"name": "color"
"type": "category"
"fields":
"suggest_2":
"type": "completion"
"contexts":
-
"name": "color"
"type": "category"
- do:
index:
index: completion_with_context
type: test
id: 1
body:
suggest_1:
input: "foo red"
contexts:
color: "red"
- do:
index:
index: completion_with_context
type: test
id: 2
body:
suggest_1:
input: "foo blue"
contexts:
color: "blue"
- do:
indices.refresh: {}
- do:
search:
index: completion_with_context
body:
suggest:
result:
prefix: "foo"
completion:
field: suggest_1.suggest_2
contexts:
color: "red"
- length: { suggest.result: 1 }
- length: { suggest.result.0.options: 1 }
- match: { suggest.result.0.options.0.text: "foo red" }
---
"Search by suggestion on sub field with weight should work":
- skip:
version: " - 6.99.99"
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
- do:
indices.create:
index: completion_with_weight
body:
mappings:
test:
"properties":
"suggest_1":
"type": "completion"
"fields":
"suggest_2":
"type": "completion"
- do:
index:
index: completion_with_weight
type: test
id: 1
body:
suggest_1:
input: "bar"
weight: 2
- do:
index:
index: completion_with_weight
type: test
id: 2
body:
suggest_1:
input: "baz"
weight: 3
- do:
indices.refresh: {}
- do:
search:
index: completion_with_weight
body:
suggest:
result:
text: "b"
completion:
field: suggest_1.suggest_2
- length: { suggest.result: 1 }
- length: { suggest.result.0.options: 2 }
- match: { suggest.result.0.options.0.text: "baz" }
- match: { suggest.result.0.options.1.text: "bar" }
---
"Search by suggestion on geofield-hash on sub field should work":
- skip:
version: " - 6.99.99"
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
- do:
indices.create:
index: geofield_with_completion
body:
mappings:
test:
"properties":
"geofield":
"type": "geo_point"
"fields":
"suggest_1":
"type": "completion"
- do:
index:
index: geofield_with_completion
type: test
id: 1
body:
geofield: "hgjhrwysvqw7"
#41.12,-72.34,12
- do:
index:
index: geofield_with_completion
type: test
id: 1
body:
geofield: "hgm4psywmkn7"
#41.12,-71.34,12
- do:
indices.refresh: {}
- do:
search:
index: geofield_with_completion
body:
suggest:
result:
prefix: "hgm"
completion:
field: geofield.suggest_1
- length: { suggest.result: 1 }
- length: { suggest.result.0.options: 1 }

View File

@ -103,6 +103,8 @@ public class Version implements Comparable<Version>, ToXContentFragment {
public static final Version V_6_4_1 = new Version(V_6_4_1_ID, org.apache.lucene.util.Version.LUCENE_7_4_0);
public static final int V_6_4_2_ID = 6040299;
public static final Version V_6_4_2 = new Version(V_6_4_2_ID, org.apache.lucene.util.Version.LUCENE_7_4_0);
public static final int V_6_4_3_ID = 6040399;
public static final Version V_6_4_3 = new Version(V_6_4_3_ID, org.apache.lucene.util.Version.LUCENE_7_4_0);
public static final int V_6_5_0_ID = 6050099;
public static final Version V_6_5_0 = new Version(V_6_5_0_ID, org.apache.lucene.util.Version.LUCENE_7_5_0);
public static final int V_7_0_0_alpha1_ID = 7000001;
@ -125,6 +127,8 @@ public class Version implements Comparable<Version>, ToXContentFragment {
return V_7_0_0_alpha1;
case V_6_5_0_ID:
return V_6_5_0;
case V_6_4_3_ID:
return V_6_4_3;
case V_6_4_2_ID:
return V_6_4_2;
case V_6_4_1_ID:

View File

@ -38,7 +38,7 @@ import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.discovery.Discovery;
import org.elasticsearch.discovery.Discovery.FailedToCommitClusterStateException;
import org.elasticsearch.discovery.MasterNotDiscoveredException;
import org.elasticsearch.node.NodeClosedException;
import org.elasticsearch.tasks.Task;
@ -53,13 +53,15 @@ import java.util.function.Supplier;
/**
* A base class for operations that needs to be performed on the master node.
*/
public abstract class TransportMasterNodeAction<Request extends MasterNodeRequest<Request>, Response extends ActionResponse> extends HandledTransportAction<Request, Response> {
public abstract class TransportMasterNodeAction<Request extends MasterNodeRequest<Request>, Response extends ActionResponse>
extends HandledTransportAction<Request, Response> {
protected final ThreadPool threadPool;
protected final TransportService transportService;
protected final ClusterService clusterService;
protected final IndexNameExpressionResolver indexNameExpressionResolver;
final String executor;
private final String executor;
protected TransportMasterNodeAction(Settings settings, String actionName, TransportService transportService,
ClusterService clusterService, ThreadPool threadPool, ActionFilters actionFilters,
@ -75,7 +77,8 @@ public abstract class TransportMasterNodeAction<Request extends MasterNodeReques
protected TransportMasterNodeAction(Settings settings, String actionName, boolean canTripCircuitBreaker,
TransportService transportService, ClusterService clusterService, ThreadPool threadPool,
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver, Supplier<Request> request) {
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver,
Supplier<Request> request) {
super(settings, actionName, canTripCircuitBreaker, transportService, actionFilters, request);
this.transportService = transportService;
this.clusterService = clusterService;
@ -138,7 +141,8 @@ public abstract class TransportMasterNodeAction<Request extends MasterNodeReques
public void start() {
ClusterState state = clusterService.state();
this.observer = new ClusterStateObserver(state, clusterService, request.masterNodeTimeout(), logger, threadPool.getThreadContext());
this.observer
= new ClusterStateObserver(state, clusterService, request.masterNodeTimeout(), logger, threadPool.getThreadContext());
doStart(state);
}
@ -174,16 +178,16 @@ public abstract class TransportMasterNodeAction<Request extends MasterNodeReques
@Override
public void onFailure(Exception t) {
if (t instanceof Discovery.FailedToCommitClusterStateException
|| (t instanceof NotMasterException)) {
logger.debug(() -> new ParameterizedMessage("master could not publish cluster state or stepped down before publishing action [{}], scheduling a retry", actionName), t);
if (t instanceof FailedToCommitClusterStateException || t instanceof NotMasterException) {
logger.debug(() -> new ParameterizedMessage("master could not publish cluster state or " +
"stepped down before publishing action [{}], scheduling a retry", actionName), t);
retry(t, masterChangePredicate);
} else {
listener.onFailure(t);
}
}
};
threadPool.executor(executor).execute(new ActionRunnable(delegate) {
threadPool.executor(executor).execute(new ActionRunnable<Response>(delegate) {
@Override
protected void doRun() throws Exception {
masterOperation(task, request, clusterState, delegate);
@ -204,7 +208,8 @@ public abstract class TransportMasterNodeAction<Request extends MasterNodeReques
Throwable cause = exp.unwrapCause();
if (cause instanceof ConnectTransportException) {
// we want to retry here a bit to see if a new master is elected
logger.debug("connection exception while trying to forward request with action name [{}] to master node [{}], scheduling a retry. Error: [{}]",
logger.debug("connection exception while trying to forward request with action name [{}] to " +
"master node [{}], scheduling a retry. Error: [{}]",
actionName, nodes.getMasterNode(), exp.getDetailedMessage());
retry(cause, masterChangePredicate);
} else {
@ -234,7 +239,8 @@ public abstract class TransportMasterNodeAction<Request extends MasterNodeReques
@Override
public void onTimeout(TimeValue timeout) {
logger.debug(() -> new ParameterizedMessage("timed out while retrying [{}] after failure (timeout [{}])", actionName, timeout), failure);
logger.debug(() -> new ParameterizedMessage("timed out while retrying [{}] after failure (timeout [{}])",
actionName, timeout), failure);
listener.onFailure(new MasterNotDiscoveredException(failure));
}
}, statePredicate

View File

@ -58,7 +58,7 @@ public interface ClusterStateTaskExecutor<T> {
* This allows groupd task description but the submitting source.
*/
default String describeTasks(List<T> tasks) {
return String.join(", ", tasks.stream().map(t -> (CharSequence)t.toString()).filter(t -> t.length() == 0)::iterator);
return String.join(", ", tasks.stream().map(t -> (CharSequence)t.toString()).filter(t -> t.length() > 0)::iterator);
}
/**

View File

@ -27,7 +27,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
/**
* This class acts as a functional wrapper around the {@code index.auto_expand_replicas} setting.
@ -93,7 +93,7 @@ public final class AutoExpandReplicas {
return Math.min(maxReplicas, numDataNodes-1);
}
Optional<Integer> getDesiredNumberOfReplicas(int numDataNodes) {
private OptionalInt getDesiredNumberOfReplicas(int numDataNodes) {
if (enabled) {
final int min = getMinReplicas();
final int max = getMaxReplicas(numDataNodes);
@ -105,10 +105,10 @@ public final class AutoExpandReplicas {
}
if (numberOfReplicas >= min && numberOfReplicas <= max) {
return Optional.of(numberOfReplicas);
return OptionalInt.of(numberOfReplicas);
}
}
return Optional.empty();
return OptionalInt.empty();
}
@Override

View File

@ -24,6 +24,7 @@ import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
@ -46,6 +47,14 @@ public interface DateFormatter {
*/
DateFormatter withZone(ZoneId zoneId);
/**
* Create a copy of this formatter that is configured to parse dates in the specified locale
*
* @param locale The local to use for the new formatter
* @return A copy of the date formatter this has been called on
*/
DateFormatter withLocale(Locale locale);
/**
* Print the supplied java time accessor in a string based representation according to this formatter
*
@ -62,6 +71,20 @@ public interface DateFormatter {
*/
String pattern();
/**
* Returns the configured locale of the date formatter
*
* @return The locale of this formatter
*/
Locale getLocale();
/**
* Returns the configured time zone of the date formatter
*
* @return The time zone of this formatter
*/
ZoneId getZone();
/**
* Configure a formatter using default fields for a TemporalAccessor that should be used in case
* the supplied date is not having all of those fields
@ -115,6 +138,11 @@ public interface DateFormatter {
return new MergedDateFormatter(Arrays.stream(formatters).map(f -> f.withZone(zoneId)).toArray(DateFormatter[]::new));
}
@Override
public DateFormatter withLocale(Locale locale) {
return new MergedDateFormatter(Arrays.stream(formatters).map(f -> f.withLocale(locale)).toArray(DateFormatter[]::new));
}
@Override
public String format(TemporalAccessor accessor) {
return formatters[0].format(accessor);
@ -125,6 +153,16 @@ public interface DateFormatter {
return format;
}
@Override
public Locale getLocale() {
return formatters[0].getLocale();
}
@Override
public ZoneId getZone() {
return formatters[0].getZone();
}
@Override
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
return new MergedDateFormatter(Arrays.stream(formatters).map(f -> f.parseDefaulting(fields)).toArray(DateFormatter[]::new));

View File

@ -1269,7 +1269,7 @@ public class DateFormatters {
return forPattern(input, Locale.ROOT);
}
public static DateFormatter forPattern(String input, Locale locale) {
private static DateFormatter forPattern(String input, Locale locale) {
if (Strings.hasLength(input)) {
input = input.trim();
}

View File

@ -25,6 +25,7 @@ import java.time.ZoneOffset;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.util.Locale;
import java.util.Map;
/**
@ -40,7 +41,8 @@ class EpochMillisDateFormatter implements DateFormatter {
public static DateFormatter INSTANCE = new EpochMillisDateFormatter();
private EpochMillisDateFormatter() {}
private EpochMillisDateFormatter() {
}
@Override
public TemporalAccessor parse(String input) {
@ -53,6 +55,17 @@ class EpochMillisDateFormatter implements DateFormatter {
@Override
public DateFormatter withZone(ZoneId zoneId) {
if (ZoneOffset.UTC.equals(zoneId) == false) {
throw new IllegalArgumentException(pattern() + " date formatter can only be in zone offset UTC");
}
return INSTANCE;
}
@Override
public DateFormatter withLocale(Locale locale) {
if (Locale.ROOT.equals(locale) == false) {
throw new IllegalArgumentException(pattern() + " date formatter can only be in locale ROOT");
}
return this;
}
@ -70,4 +83,14 @@ class EpochMillisDateFormatter implements DateFormatter {
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
return this;
}
@Override
public Locale getLocale() {
return Locale.ROOT;
}
@Override
public ZoneId getZone() {
return ZoneOffset.UTC;
}
}

View File

@ -26,6 +26,7 @@ import java.time.ZoneOffset;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
@ -59,11 +60,6 @@ public class EpochSecondsDateFormatter implements DateFormatter {
}
}
@Override
public DateFormatter withZone(ZoneId zoneId) {
return this;
}
@Override
public String format(TemporalAccessor accessor) {
Instant instant = Instant.from(accessor);
@ -75,7 +71,33 @@ public class EpochSecondsDateFormatter implements DateFormatter {
@Override
public String pattern() {
return "epoch_seconds";
return "epoch_second";
}
@Override
public Locale getLocale() {
return Locale.ROOT;
}
@Override
public ZoneId getZone() {
return ZoneOffset.UTC;
}
@Override
public DateFormatter withZone(ZoneId zoneId) {
if (zoneId.equals(ZoneOffset.UTC) == false) {
throw new IllegalArgumentException(pattern() + " date formatter can only be in zone offset UTC");
}
return this;
}
@Override
public DateFormatter withLocale(Locale locale) {
if (Locale.ROOT.equals(locale) == false) {
throw new IllegalArgumentException(pattern() + " date formatter can only be in locale ROOT");
}
return this;
}
@Override

View File

@ -28,6 +28,7 @@ import java.time.temporal.TemporalField;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
class JavaDateFormatter implements DateFormatter {
@ -36,10 +37,17 @@ class JavaDateFormatter implements DateFormatter {
private final DateTimeFormatter[] parsers;
JavaDateFormatter(String format, DateTimeFormatter printer, DateTimeFormatter... parsers) {
if (printer == null) {
throw new IllegalArgumentException("printer may not be null");
}
long distinctZones = Arrays.stream(parsers).map(DateTimeFormatter::getZone).distinct().count();
if (distinctZones > 1) {
throw new IllegalArgumentException("formatters must have the same time zone");
}
long distinctLocales = Arrays.stream(parsers).map(DateTimeFormatter::getLocale).distinct().count();
if (distinctLocales > 1) {
throw new IllegalArgumentException("formatters must have the same locale");
}
if (parsers.length == 0) {
this.parsers = new DateTimeFormatter[]{printer};
} else {
@ -83,6 +91,21 @@ class JavaDateFormatter implements DateFormatter {
return new JavaDateFormatter(format, printer.withZone(zoneId), parsersWithZone);
}
@Override
public DateFormatter withLocale(Locale locale) {
// shortcurt to not create new objects unnecessarily
if (locale.equals(parsers[0].getLocale())) {
return this;
}
final DateTimeFormatter[] parsersWithZone = new DateTimeFormatter[parsers.length];
for (int i = 0; i < parsers.length; i++) {
parsersWithZone[i] = parsers[i].withLocale(locale);
}
return new JavaDateFormatter(format, printer.withLocale(locale), parsersWithZone);
}
@Override
public String format(TemporalAccessor accessor) {
return printer.format(accessor);
@ -109,4 +132,36 @@ class JavaDateFormatter implements DateFormatter {
return new JavaDateFormatter(format, parseDefaultingBuilder.toFormatter(Locale.ROOT), parsersWithDefaulting);
}
}
@Override
public Locale getLocale() {
return this.printer.getLocale();
}
@Override
public ZoneId getZone() {
return this.printer.getZone();
}
@Override
public int hashCode() {
return Objects.hash(getLocale(), printer.getZone(), format);
}
@Override
public boolean equals(Object obj) {
if (obj.getClass().equals(this.getClass()) == false) {
return false;
}
JavaDateFormatter other = (JavaDateFormatter) obj;
return Objects.equals(format, other.format) &&
Objects.equals(getLocale(), other.getLocale()) &&
Objects.equals(this.printer.getZone(), other.printer.getZone());
}
@Override
public String toString() {
return String.format(Locale.ROOT, "format[%s] locale[%s]", format, getLocale());
}
}

View File

@ -436,8 +436,9 @@ public class CompletionFieldMapper extends FieldMapper implements ArrayValueMapp
Token token = parser.currentToken();
Map<String, CompletionInputMetaData> inputMap = new HashMap<>(1);
// ignore null values
if (token == Token.VALUE_NULL) {
if (context.externalValueSet()) {
inputMap = getInputMapFromExternalValue(context);
} else if (token == Token.VALUE_NULL) { // ignore null values
return;
} else if (token == Token.START_ARRAY) {
while ((token = parser.nextToken()) != Token.END_ARRAY) {
@ -471,12 +472,33 @@ public class CompletionFieldMapper extends FieldMapper implements ArrayValueMapp
context.doc().add(new SuggestField(fieldType().name(), input, metaData.weight));
}
}
List<IndexableField> fields = new ArrayList<>(1);
createFieldNamesField(context, fields);
for (IndexableField field : fields) {
context.doc().add(field);
}
multiFields.parse(this, context);
for (CompletionInputMetaData metaData: inputMap.values()) {
ParseContext externalValueContext = context.createExternalValueContext(metaData);
multiFields.parse(this, externalValueContext);
}
}
private Map<String, CompletionInputMetaData> getInputMapFromExternalValue(ParseContext context) {
Map<String, CompletionInputMetaData> inputMap;
if (isExternalValueOfClass(context, CompletionInputMetaData.class)) {
CompletionInputMetaData inputAndMeta = (CompletionInputMetaData) context.externalValue();
inputMap = Collections.singletonMap(inputAndMeta.input, inputAndMeta);
} else {
String fieldName = context.externalValue().toString();
inputMap = Collections.singletonMap(fieldName, new CompletionInputMetaData(fieldName, Collections.emptyMap(), 1));
}
return inputMap;
}
private boolean isExternalValueOfClass(ParseContext context, Class<?> clazz) {
return context.externalValue().getClass().equals(clazz);
}
/**
@ -487,7 +509,7 @@ public class CompletionFieldMapper extends FieldMapper implements ArrayValueMapp
private void parse(ParseContext parseContext, Token token, XContentParser parser, Map<String, CompletionInputMetaData> inputMap) throws IOException {
String currentFieldName = null;
if (token == Token.VALUE_STRING) {
inputMap.put(parser.text(), new CompletionInputMetaData(Collections.<String, Set<CharSequence>>emptyMap(), 1));
inputMap.put(parser.text(), new CompletionInputMetaData(parser.text(), Collections.emptyMap(), 1));
} else if (token == Token.START_OBJECT) {
Set<String> inputs = new HashSet<>();
int weight = 1;
@ -561,7 +583,7 @@ public class CompletionFieldMapper extends FieldMapper implements ArrayValueMapp
}
for (String input : inputs) {
if (inputMap.containsKey(input) == false || inputMap.get(input).weight < weight) {
inputMap.put(input, new CompletionInputMetaData(contextsMap, weight));
inputMap.put(input, new CompletionInputMetaData(input, contextsMap, weight));
}
}
} else {
@ -570,13 +592,20 @@ public class CompletionFieldMapper extends FieldMapper implements ArrayValueMapp
}
static class CompletionInputMetaData {
public final String input;
public final Map<String, Set<CharSequence>> contexts;
public final int weight;
CompletionInputMetaData(Map<String, Set<CharSequence>> contexts, int weight) {
CompletionInputMetaData(String input, Map<String, Set<CharSequence>> contexts, int weight) {
this.input = input;
this.contexts = contexts;
this.weight = weight;
}
@Override
public String toString() {
return input;
}
}
@Override

View File

@ -67,7 +67,9 @@ public class MultiMatchQuery extends MatchQuery {
}
public Query parse(MultiMatchQueryBuilder.Type type, Map<String, Float> fieldNames, Object value, String minimumShouldMatch) throws IOException {
Query result;
final Query result;
// reset query builder
queryBuilder = null;
if (fieldNames.size() == 1) {
Map.Entry<String, Float> fieldBoost = fieldNames.entrySet().iterator().next();
Float boostValue = fieldBoost.getValue();

View File

@ -19,6 +19,7 @@
package org.elasticsearch.index.search;
import org.apache.lucene.index.PrefixCodedTerms;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.BoostQuery;
@ -28,6 +29,7 @@ import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.PointRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermInSetQuery;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.BooleanClause.Occur;
import org.elasticsearch.index.mapper.MapperService;
@ -54,9 +56,18 @@ public final class NestedHelper {
} else if (query instanceof MatchNoDocsQuery) {
return false;
} else if (query instanceof TermQuery) {
// We only handle term queries and range queries, which should already
// We only handle term(s) queries and range queries, which should already
// cover a high majority of use-cases
return mightMatchNestedDocs(((TermQuery) query).getTerm().field());
} else if (query instanceof TermInSetQuery) {
PrefixCodedTerms terms = ((TermInSetQuery) query).getTermData();
if (terms.size() > 0) {
PrefixCodedTerms.TermIterator it = terms.iterator();
it.next();
return mightMatchNestedDocs(it.field());
} else {
return false;
}
} else if (query instanceof PointRangeQuery) {
return mightMatchNestedDocs(((PointRangeQuery) query).getField());
} else if (query instanceof IndexOrDocValuesQuery) {
@ -118,6 +129,15 @@ public final class NestedHelper {
return false;
} else if (query instanceof TermQuery) {
return mightMatchNonNestedDocs(((TermQuery) query).getTerm().field(), nestedPath);
} else if (query instanceof TermInSetQuery) {
PrefixCodedTerms terms = ((TermInSetQuery) query).getTermData();
if (terms.size() > 0) {
PrefixCodedTerms.TermIterator it = terms.iterator();
it.next();
return mightMatchNonNestedDocs(it.field(), nestedPath);
} else {
return false;
}
} else if (query instanceof PointRangeQuery) {
return mightMatchNonNestedDocs(((PointRangeQuery) query).getField(), nestedPath);
} else if (query instanceof IndexOrDocValuesQuery) {

View File

@ -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.cluster;
import org.elasticsearch.test.ESTestCase;
import java.util.Arrays;
import java.util.Collections;
import static org.hamcrest.Matchers.equalTo;
public class ClusterStateTaskExecutorTests extends ESTestCase {
private class TestTask {
private final String description;
TestTask(String description) {
this.description = description;
}
@Override
public String toString() {
return description == null ? "" : "Task{" + description + "}";
}
}
public void testDescribeTasks() {
final ClusterStateTaskExecutor<TestTask> executor = (currentState, tasks) -> {
throw new AssertionError("should not be called");
};
assertThat("describes an empty list", executor.describeTasks(Collections.emptyList()), equalTo(""));
assertThat("describes a singleton list", executor.describeTasks(Collections.singletonList(new TestTask("a task"))),
equalTo("Task{a task}"));
assertThat("describes a list of two tasks",
executor.describeTasks(Arrays.asList(new TestTask("a task"), new TestTask("another task"))),
equalTo("Task{a task}, Task{another task}"));
assertThat("skips the only item if it has no description", executor.describeTasks(Collections.singletonList(new TestTask(null))),
equalTo(""));
assertThat("skips an item if it has no description",
executor.describeTasks(Arrays.asList(
new TestTask("a task"), new TestTask(null), new TestTask("another task"), new TestTask(null))),
equalTo("Task{a task}, Task{another task}"));
}
}

View File

@ -23,38 +23,23 @@ import org.elasticsearch.test.ESTestCase;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.util.Locale;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
public class DateFormattersTests extends ESTestCase {
public void testEpochMilliParser() {
DateFormatter formatter = DateFormatters.forPattern("epoch_millis");
DateTimeParseException e = expectThrows(DateTimeParseException.class, () -> formatter.parse("invalid"));
assertThat(e.getMessage(), containsString("invalid number"));
// different zone, should still yield the same output, as epoch is time zone independent
ZoneId zoneId = randomZone();
DateFormatter zonedFormatter = formatter.withZone(zoneId);
// test with negative and non negative values
assertThatSameDateTime(formatter, zonedFormatter, randomNonNegativeLong() * -1);
assertThatSameDateTime(formatter, zonedFormatter, randomNonNegativeLong());
assertThatSameDateTime(formatter, zonedFormatter, 0);
assertThatSameDateTime(formatter, zonedFormatter, -1);
assertThatSameDateTime(formatter, zonedFormatter, 1);
// format() output should be equal as well
assertSameFormat(formatter, randomNonNegativeLong() * -1);
assertSameFormat(formatter, randomNonNegativeLong());
assertSameFormat(formatter, 0);
assertSameFormat(formatter, -1);
assertSameFormat(formatter, 1);
}
// this is not in the duelling tests, because the epoch second parser in joda time drops the milliseconds after the comma
@ -83,14 +68,6 @@ public class DateFormattersTests extends ESTestCase {
assertThat(e.getMessage(), is("invalid number [abc]"));
e = expectThrows(DateTimeParseException.class, () -> formatter.parse("1234.abc"));
assertThat(e.getMessage(), is("invalid number [1234.abc]"));
// different zone, should still yield the same output, as epoch is time zone independent
ZoneId zoneId = randomZone();
DateFormatter zonedFormatter = formatter.withZone(zoneId);
assertThatSameDateTime(formatter, zonedFormatter, randomLongBetween(-100_000_000, 100_000_000));
assertSameFormat(formatter, randomLongBetween(-100_000_000, 100_000_000));
assertThat(formatter.format(Instant.ofEpochSecond(1234, 567_000_000)), is("1234.567"));
}
public void testEpochMilliParsersWithDifferentFormatters() {
@ -100,16 +77,54 @@ public class DateFormattersTests extends ESTestCase {
assertThat(formatter.pattern(), is("strict_date_optional_time||epoch_millis"));
}
private void assertThatSameDateTime(DateFormatter formatter, DateFormatter zonedFormatter, long millis) {
String millisAsString = String.valueOf(millis);
ZonedDateTime formatterZonedDateTime = DateFormatters.toZonedDateTime(formatter.parse(millisAsString));
ZonedDateTime zonedFormatterZonedDateTime = DateFormatters.toZonedDateTime(zonedFormatter.parse(millisAsString));
assertThat(formatterZonedDateTime.toInstant().toEpochMilli(), is(zonedFormatterZonedDateTime.toInstant().toEpochMilli()));
public void testLocales() {
assertThat(DateFormatters.forPattern("strict_date_optional_time").getLocale(), is(Locale.ROOT));
Locale locale = randomLocale(random());
assertThat(DateFormatters.forPattern("strict_date_optional_time").withLocale(locale).getLocale(), is(locale));
IllegalArgumentException e =
expectThrows(IllegalArgumentException.class, () -> DateFormatters.forPattern("epoch_millis").withLocale(locale));
assertThat(e.getMessage(), is("epoch_millis date formatter can only be in locale ROOT"));
e = expectThrows(IllegalArgumentException.class, () -> DateFormatters.forPattern("epoch_second").withLocale(locale));
assertThat(e.getMessage(), is("epoch_second date formatter can only be in locale ROOT"));
}
private void assertSameFormat(DateFormatter formatter, long millis) {
String millisAsString = String.valueOf(millis);
TemporalAccessor accessor = formatter.parse(millisAsString);
assertThat(millisAsString, is(formatter.format(accessor)));
public void testTimeZones() {
// zone is null by default due to different behaviours between java8 and above
assertThat(DateFormatters.forPattern("strict_date_optional_time").getZone(), is(nullValue()));
ZoneId zoneId = randomZone();
assertThat(DateFormatters.forPattern("strict_date_optional_time").withZone(zoneId).getZone(), is(zoneId));
IllegalArgumentException e =
expectThrows(IllegalArgumentException.class, () -> DateFormatters.forPattern("epoch_millis").withZone(zoneId));
assertThat(e.getMessage(), is("epoch_millis date formatter can only be in zone offset UTC"));
e = expectThrows(IllegalArgumentException.class, () -> DateFormatters.forPattern("epoch_second").withZone(zoneId));
assertThat(e.getMessage(), is("epoch_second date formatter can only be in zone offset UTC"));
}
public void testEqualsAndHashcode() {
assertThat(DateFormatters.forPattern("strict_date_optional_time"),
sameInstance(DateFormatters.forPattern("strict_date_optional_time")));
assertThat(DateFormatters.forPattern("YYYY"), equalTo(DateFormatters.forPattern("YYYY")));
assertThat(DateFormatters.forPattern("YYYY").hashCode(),
is(DateFormatters.forPattern("YYYY").hashCode()));
// different timezone, thus not equals
assertThat(DateFormatters.forPattern("YYYY").withZone(ZoneId.of("CET")), not(equalTo(DateFormatters.forPattern("YYYY"))));
// different locale, thus not equals
assertThat(DateFormatters.forPattern("YYYY").withLocale(randomLocale(random())),
not(equalTo(DateFormatters.forPattern("YYYY"))));
// different pattern, thus not equals
assertThat(DateFormatters.forPattern("YYYY"), not(equalTo(DateFormatters.forPattern("YY"))));
DateFormatter epochSecondFormatter = DateFormatters.forPattern("epoch_second");
assertThat(epochSecondFormatter, sameInstance(DateFormatters.forPattern("epoch_second")));
assertThat(epochSecondFormatter, equalTo(DateFormatters.forPattern("epoch_second")));
assertThat(epochSecondFormatter.hashCode(), is(DateFormatters.forPattern("epoch_second").hashCode()));
DateFormatter epochMillisFormatter = DateFormatters.forPattern("epoch_millis");
assertThat(epochMillisFormatter.hashCode(), is(DateFormatters.forPattern("epoch_millis").hashCode()));
assertThat(epochMillisFormatter, sameInstance(DateFormatters.forPattern("epoch_millis")));
assertThat(epochMillisFormatter, equalTo(DateFormatters.forPattern("epoch_millis")));
}
}

View File

@ -18,9 +18,11 @@
*/
package org.elasticsearch.index.mapper;
import org.apache.lucene.document.SortedSetDocValuesField;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.suggest.document.CompletionAnalyzer;
import org.apache.lucene.search.suggest.document.ContextSuggestField;
import org.apache.lucene.search.suggest.document.FuzzyCompletionQuery;
import org.apache.lucene.search.suggest.document.PrefixCompletionQuery;
import org.apache.lucene.search.suggest.document.RegexCompletionQuery;
@ -42,11 +44,18 @@ import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.index.IndexService;
import org.elasticsearch.index.analysis.NamedAnalyzer;
import org.elasticsearch.test.ESSingleNodeTestCase;
import org.hamcrest.FeatureMatcher;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.hamcrest.core.CombinableMatcher;
import java.io.IOException;
import java.util.Map;
import java.util.function.Function;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
@ -182,6 +191,328 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
assertEquals("failed to parse [completion]: expected text or object, but got VALUE_NUMBER", e.getCause().getMessage());
}
public void testKeywordWithSubCompletionAndContext() throws Exception {
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
.startObject("properties")
.startObject("keywordfield")
.field("type", "keyword")
.startObject("fields")
.startObject("subsuggest")
.field("type", "completion")
.startArray("contexts")
.startObject()
.field("name","place_type")
.field("type","category")
.field("path","cat")
.endObject()
.endArray()
.endObject()
.endObject()
.endObject().endObject()
.endObject().endObject()
);
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
.bytes(XContentFactory.jsonBuilder()
.startObject()
.array("keywordfield", "key1", "key2", "key3")
.endObject()),
XContentType.JSON));
ParseContext.Document indexableFields = parsedDocument.rootDoc();
assertThat(indexableFields.getFields("keywordfield"), arrayContainingInAnyOrder(
keywordField("key1"),
sortedSetDocValuesField("key1"),
keywordField("key2"),
sortedSetDocValuesField("key2"),
keywordField("key3"),
sortedSetDocValuesField("key3")
));
assertThat(indexableFields.getFields("keywordfield.subsuggest"), arrayContainingInAnyOrder(
contextSuggestField("key1"),
contextSuggestField("key2"),
contextSuggestField("key3")
));
}
public void testCompletionWithContextAndSubCompletion() throws Exception {
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
.startObject("properties")
.startObject("suggest")
.field("type", "completion")
.startArray("contexts")
.startObject()
.field("name","place_type")
.field("type","category")
.field("path","cat")
.endObject()
.endArray()
.startObject("fields")
.startObject("subsuggest")
.field("type", "completion")
.startArray("contexts")
.startObject()
.field("name","place_type")
.field("type","category")
.field("path","cat")
.endObject()
.endArray()
.endObject()
.endObject()
.endObject().endObject()
.endObject().endObject()
);
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
.bytes(XContentFactory.jsonBuilder()
.startObject()
.startObject("suggest")
.array("input","timmy","starbucks")
.startObject("contexts")
.array("place_type","cafe","food")
.endObject()
.field("weight", 3)
.endObject()
.endObject()),
XContentType.JSON));
ParseContext.Document indexableFields = parsedDocument.rootDoc();
assertThat(indexableFields.getFields("suggest"), arrayContainingInAnyOrder(
contextSuggestField("timmy"),
contextSuggestField("starbucks")
));
assertThat(indexableFields.getFields("suggest.subsuggest"), arrayContainingInAnyOrder(
contextSuggestField("timmy"),
contextSuggestField("starbucks")
));
//unable to assert about context, covered in a REST test
}
public void testCompletionWithContextAndSubCompletionIndexByPath() throws Exception {
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
.startObject("properties")
.startObject("suggest")
.field("type", "completion")
.startArray("contexts")
.startObject()
.field("name","place_type")
.field("type","category")
.field("path","cat")
.endObject()
.endArray()
.startObject("fields")
.startObject("subsuggest")
.field("type", "completion")
.startArray("contexts")
.startObject()
.field("name","place_type")
.field("type","category")
.field("path","cat")
.endObject()
.endArray()
.endObject()
.endObject()
.endObject().endObject()
.endObject().endObject()
);
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
.bytes(XContentFactory.jsonBuilder()
.startObject()
.array("suggest", "timmy","starbucks")
.array("cat","cafe","food")
.endObject()),
XContentType.JSON));
ParseContext.Document indexableFields = parsedDocument.rootDoc();
assertThat(indexableFields.getFields("suggest"), arrayContainingInAnyOrder(
contextSuggestField("timmy"),
contextSuggestField("starbucks")
));
assertThat(indexableFields.getFields("suggest.subsuggest"), arrayContainingInAnyOrder(
contextSuggestField("timmy"),
contextSuggestField("starbucks")
));
//unable to assert about context, covered in a REST test
}
public void testKeywordWithSubCompletionAndStringInsert() throws Exception {
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
.startObject("properties").startObject("geofield")
.field("type", "geo_point")
.startObject("fields")
.startObject("analyzed")
.field("type", "completion")
.endObject()
.endObject()
.endObject().endObject()
.endObject().endObject()
);
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
.bytes(XContentFactory.jsonBuilder()
.startObject()
.field("geofield", "drm3btev3e86")//"41.12,-71.34"
.endObject()),
XContentType.JSON));
ParseContext.Document indexableFields = parsedDocument.rootDoc();
assertThat(indexableFields.getFields("geofield"), arrayWithSize(2));
assertThat(indexableFields.getFields("geofield.analyzed"), arrayContainingInAnyOrder(
suggestField("drm3btev3e86")
));
//unable to assert about geofield content, covered in a REST test
}
public void testCompletionTypeWithSubCompletionFieldAndStringInsert() throws Exception {
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
.startObject("properties").startObject("suggest")
.field("type", "completion")
.startObject("fields")
.startObject("subsuggest")
.field("type", "completion")
.endObject()
.endObject()
.endObject().endObject()
.endObject().endObject()
);
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
.bytes(XContentFactory.jsonBuilder()
.startObject()
.field("suggest", "suggestion")
.endObject()),
XContentType.JSON));
ParseContext.Document indexableFields = parsedDocument.rootDoc();
assertThat(indexableFields.getFields("suggest"), arrayContainingInAnyOrder(
suggestField("suggestion")
));
assertThat(indexableFields.getFields("suggest.subsuggest"), arrayContainingInAnyOrder(
suggestField("suggestion")
));
}
public void testCompletionTypeWithSubCompletionFieldAndObjectInsert() throws Exception {
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
.startObject("properties").startObject("completion")
.field("type", "completion")
.startObject("fields")
.startObject("analyzed")
.field("type", "completion")
.endObject()
.endObject()
.endObject().endObject()
.endObject().endObject()
);
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
.bytes(XContentFactory.jsonBuilder()
.startObject()
.startObject("completion")
.array("input","New York", "NY")
.field("weight",34)
.endObject()
.endObject()),
XContentType.JSON));
ParseContext.Document indexableFields = parsedDocument.rootDoc();
assertThat(indexableFields.getFields("completion"), arrayContainingInAnyOrder(
suggestField("New York"),
suggestField("NY")
));
assertThat(indexableFields.getFields("completion.analyzed"), arrayContainingInAnyOrder(
suggestField("New York"),
suggestField("NY")
));
//unable to assert about weight, covered in a REST test
}
public void testCompletionTypeWithSubKeywordFieldAndObjectInsert() throws Exception {
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
.startObject("properties").startObject("completion")
.field("type", "completion")
.startObject("fields")
.startObject("analyzed")
.field("type", "keyword")
.endObject()
.endObject()
.endObject().endObject()
.endObject().endObject()
);
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
.bytes(XContentFactory.jsonBuilder()
.startObject()
.startObject("completion")
.array("input","New York", "NY")
.field("weight",34)
.endObject()
.endObject()),
XContentType.JSON));
ParseContext.Document indexableFields = parsedDocument.rootDoc();
assertThat(indexableFields.getFields("completion"), arrayContainingInAnyOrder(
suggestField("New York"),
suggestField("NY")
));
assertThat(indexableFields.getFields("completion.analyzed"), arrayContainingInAnyOrder(
keywordField("New York"),
sortedSetDocValuesField("New York"),
keywordField("NY"),
sortedSetDocValuesField("NY")
));
//unable to assert about weight, covered in a REST test
}
public void testCompletionTypeWithSubKeywordFieldAndStringInsert() throws Exception {
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
.startObject("properties").startObject("completion")
.field("type", "completion")
.startObject("fields")
.startObject("analyzed")
.field("type", "keyword")
.endObject()
.endObject()
.endObject().endObject()
.endObject().endObject()
);
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
.bytes(XContentFactory.jsonBuilder()
.startObject()
.field("completion", "suggestion")
.endObject()),
XContentType.JSON));
ParseContext.Document indexableFields = parsedDocument.rootDoc();
assertThat(indexableFields.getFields("completion"), arrayContainingInAnyOrder(
suggestField("suggestion")
));
assertThat(indexableFields.getFields("completion.analyzed"), arrayContainingInAnyOrder(
keywordField("suggestion"),
sortedSetDocValuesField("suggestion")
));
}
public void testParsingMultiValued() throws Exception {
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
.startObject("properties").startObject("completion")
@ -199,7 +530,10 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
.endObject()),
XContentType.JSON));
IndexableField[] fields = parsedDocument.rootDoc().getFields(fieldMapper.name());
assertSuggestFields(fields, 2);
assertThat(fields, arrayContainingInAnyOrder(
suggestField("suggestion1"),
suggestField("suggestion2")
));
}
public void testParsingWithWeight() throws Exception {
@ -222,7 +556,9 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
.endObject()),
XContentType.JSON));
IndexableField[] fields = parsedDocument.rootDoc().getFields(fieldMapper.name());
assertSuggestFields(fields, 1);
assertThat(fields, arrayContainingInAnyOrder(
suggestField("suggestion")
));
}
public void testParsingMultiValueWithWeight() throws Exception {
@ -245,7 +581,11 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
.endObject()),
XContentType.JSON));
IndexableField[] fields = parsedDocument.rootDoc().getFields(fieldMapper.name());
assertSuggestFields(fields, 3);
assertThat(fields, arrayContainingInAnyOrder(
suggestField("suggestion1"),
suggestField("suggestion2"),
suggestField("suggestion3")
));
}
public void testParsingWithGeoFieldAlias() throws Exception {
@ -318,7 +658,11 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
.endObject()),
XContentType.JSON));
IndexableField[] fields = parsedDocument.rootDoc().getFields(fieldMapper.name());
assertSuggestFields(fields, 3);
assertThat(fields, arrayContainingInAnyOrder(
suggestField("suggestion1"),
suggestField("suggestion2"),
suggestField("suggestion3")
));
}
public void testParsingMixed() throws Exception {
@ -351,7 +695,14 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
.endObject()),
XContentType.JSON));
IndexableField[] fields = parsedDocument.rootDoc().getFields(fieldMapper.name());
assertSuggestFields(fields, 6);
assertThat(fields, arrayContainingInAnyOrder(
suggestField("suggestion1"),
suggestField("suggestion2"),
suggestField("suggestion3"),
suggestField("suggestion4"),
suggestField("suggestion5"),
suggestField("suggestion6")
));
}
public void testNonContextEnabledParsingWithContexts() throws Exception {
@ -508,9 +859,13 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
}
private static void assertSuggestFields(IndexableField[] fields, int expected) {
assertFieldsOfType(fields, SuggestField.class, expected);
}
private static void assertFieldsOfType(IndexableField[] fields, Class<?> clazz, int expected) {
int actualFieldCount = 0;
for (IndexableField field : fields) {
if (field instanceof SuggestField) {
if (clazz.isInstance(field)) {
actualFieldCount++;
}
}
@ -529,4 +884,33 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
);
assertThat(e.getMessage(), containsString("name cannot be empty string"));
}
private Matcher<IndexableField> suggestField(String value) {
return Matchers.allOf(hasProperty(IndexableField::stringValue, equalTo(value)),
Matchers.instanceOf(SuggestField.class));
}
private Matcher<IndexableField> contextSuggestField(String value) {
return Matchers.allOf(hasProperty(IndexableField::stringValue, equalTo(value)),
Matchers.instanceOf(ContextSuggestField.class));
}
private CombinableMatcher<IndexableField> sortedSetDocValuesField(String value) {
return Matchers.both(hasProperty(IndexableField::binaryValue, equalTo(new BytesRef(value))))
.and(Matchers.instanceOf(SortedSetDocValuesField.class));
}
private CombinableMatcher<IndexableField> keywordField(String value) {
return Matchers.both(hasProperty(IndexableField::binaryValue, equalTo(new BytesRef(value))))
.and(hasProperty(IndexableField::fieldType, Matchers.instanceOf(KeywordFieldMapper.KeywordFieldType.class)));
}
private <T, V> Matcher<T> hasProperty(Function<? super T, ? extends V> property, Matcher<V> valueMatcher) {
return new FeatureMatcher<T, V>(valueMatcher, "object with", property.toString()) {
@Override
protected V featureValueOf(T actual) {
return property.apply(actual);
}
};
}
}

View File

@ -21,6 +21,7 @@ package org.elasticsearch.index.query;
import org.apache.lucene.analysis.MockSynonymAnalyzer;
import org.apache.lucene.index.Term;
import org.apache.lucene.queries.BlendedTermQuery;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
@ -1195,20 +1196,23 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
newIndexMeta("index", context.getIndexSettings().getSettings(), Settings.builder().putList("index.query.default_field",
STRING_FIELD_NAME, STRING_FIELD_NAME_2 + "^5").build())
);
Query query = new QueryStringQueryBuilder("hello")
.toQuery(context);
Query expected = new DisjunctionMaxQuery(
Arrays.asList(
new TermQuery(new Term(STRING_FIELD_NAME, "hello")),
new BoostQuery(new TermQuery(new Term(STRING_FIELD_NAME_2, "hello")), 5.0f)
), 0.0f
);
assertEquals(expected, query);
// Reset the default value
context.getIndexSettings().updateIndexMetaData(
newIndexMeta("index",
context.getIndexSettings().getSettings(), Settings.builder().putList("index.query.default_field", "*").build())
);
try {
Query query = new QueryStringQueryBuilder("hello")
.toQuery(context);
Query expected = new DisjunctionMaxQuery(
Arrays.asList(
new TermQuery(new Term(STRING_FIELD_NAME, "hello")),
new BoostQuery(new TermQuery(new Term(STRING_FIELD_NAME_2, "hello")), 5.0f)
), 0.0f
);
assertEquals(expected, query);
} finally {
// Reset the default value
context.getIndexSettings().updateIndexMetaData(
newIndexMeta("index",
context.getIndexSettings().getSettings(), Settings.builder().putList("index.query.default_field", "*").build())
);
}
}
/**
@ -1345,6 +1349,44 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
assertEquals(expected, query);
}
public void testCrossFields() throws Exception {
final QueryShardContext context = createShardContext();
context.getIndexSettings().updateIndexMetaData(
newIndexMeta("index", context.getIndexSettings().getSettings(),
Settings.builder().putList("index.query.default_field",
STRING_FIELD_NAME, STRING_FIELD_NAME_2).build())
);
try {
Term[] blendedTerms = new Term[2];
blendedTerms[0] = new Term(STRING_FIELD_NAME, "foo");
blendedTerms[1] = new Term(STRING_FIELD_NAME_2, "foo");
Query query = new QueryStringQueryBuilder("foo")
.analyzer("whitespace")
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
.toQuery(createShardContext());
Query expected = BlendedTermQuery.dismaxBlendedQuery(blendedTerms, 1.0f);
assertEquals(expected, query);
query = new QueryStringQueryBuilder("foo mapped_string:10")
.analyzer("whitespace")
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
.toQuery(createShardContext());
expected = new BooleanQuery.Builder()
.add(BlendedTermQuery.dismaxBlendedQuery(blendedTerms, 1.0f), Occur.SHOULD)
.add(new TermQuery(new Term(STRING_FIELD_NAME, "10")), Occur.SHOULD)
.build();
assertEquals(expected, query);
} finally {
// Reset the default value
context.getIndexSettings().updateIndexMetaData(
newIndexMeta("index",
context.getIndexSettings().getSettings(),
Settings.builder().putList("index.query.default_field", "*").build())
);
}
}
private static IndexMetaData newIndexMeta(String name, Settings oldIndexSettings, Settings indexSettings) {
Settings build = Settings.builder().put(oldIndexSettings)
.put(indexSettings)

View File

@ -40,6 +40,7 @@ import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.test.ESSingleNodeTestCase;
import java.io.IOException;
import java.util.Collections;
public class NestedHelperTests extends ESSingleNodeTestCase {
@ -115,6 +116,36 @@ public class NestedHelperTests extends ESSingleNodeTestCase {
assertFalse(new NestedHelper(mapperService).mightMatchNonNestedDocs(new MatchNoDocsQuery(), "nested_missing"));
}
public void testTermsQuery() {
Query termsQuery = mapperService.fullName("foo").termsQuery(Collections.singletonList("bar"), null);
assertFalse(new NestedHelper(mapperService).mightMatchNestedDocs(termsQuery));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested1"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested2"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested3"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested_missing"));
termsQuery = mapperService.fullName("nested1.foo").termsQuery(Collections.singletonList("bar"), null);
assertTrue(new NestedHelper(mapperService).mightMatchNestedDocs(termsQuery));
assertFalse(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested1"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested2"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested3"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested_missing"));
termsQuery = mapperService.fullName("nested2.foo").termsQuery(Collections.singletonList("bar"), null);
assertTrue(new NestedHelper(mapperService).mightMatchNestedDocs(termsQuery));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested1"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested2"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested3"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested_missing"));
termsQuery = mapperService.fullName("nested3.foo").termsQuery(Collections.singletonList("bar"), null);
assertTrue(new NestedHelper(mapperService).mightMatchNestedDocs(termsQuery));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested1"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested2"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested3"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested_missing"));
}
public void testTermQuery() {
Query termQuery = mapperService.fullName("foo").termQuery("bar", null);
assertFalse(new NestedHelper(mapperService).mightMatchNestedDocs(termQuery));

View File

@ -20,6 +20,7 @@
package org.elasticsearch.test;
import org.elasticsearch.common.CheckedBiFunction;
import org.elasticsearch.common.CheckedBiConsumer;
import org.elasticsearch.common.CheckedFunction;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
@ -38,34 +39,147 @@ import java.util.function.Supplier;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent;
public abstract class AbstractXContentTestCase<T extends ToXContent> extends ESTestCase {
public abstract class AbstractXContentTestCase<T extends ToXContent> extends ESTestCase {
protected static final int NUMBER_OF_TEST_RUNS = 20;
public static <T extends ToXContent> void testFromXContent(int numberOfTestRuns, Supplier<T> instanceSupplier,
boolean supportsUnknownFields, String[] shuffleFieldsExceptions,
Predicate<String> randomFieldsExcludeFilter,
CheckedBiFunction<XContent, BytesReference, XContentParser, IOException>
createParserFunction,
CheckedFunction<XContentParser, T, IOException> parseFunction,
BiConsumer<T, T> assertEqualsConsumer,
boolean assertToXContentEquivalence,
ToXContent.Params toXContentParams) throws IOException {
for (int runs = 0; runs < numberOfTestRuns; runs++) {
T testInstance = instanceSupplier.get();
XContentType xContentType = randomFrom(XContentType.values());
BytesReference shuffledContent = insertRandomFieldsAndShuffle(testInstance, xContentType, supportsUnknownFields,
shuffleFieldsExceptions, randomFieldsExcludeFilter, createParserFunction, toXContentParams);
XContentParser parser = createParserFunction.apply(XContentFactory.xContent(xContentType), shuffledContent);
T parsed = parseFunction.apply(parser);
assertEqualsConsumer.accept(testInstance, parsed);
if (assertToXContentEquivalence) {
assertToXContentEquivalent(
public static <T> XContentTester<T> xContentTester(
CheckedBiFunction<XContent, BytesReference, XContentParser, IOException> createParser,
Supplier<T> instanceSupplier,
CheckedBiConsumer<T, XContentBuilder, IOException> toXContent,
CheckedFunction<XContentParser, T, IOException> fromXContent) {
return new XContentTester<T>(
createParser,
instanceSupplier,
(testInstance, xContentType) -> {
try (XContentBuilder builder = XContentBuilder.builder(xContentType.xContent())) {
toXContent.accept(testInstance, builder);
return BytesReference.bytes(builder);
}
},
fromXContent);
}
public static <T extends ToXContent> XContentTester<T> xContentTester(
CheckedBiFunction<XContent, BytesReference, XContentParser, IOException> createParser,
Supplier<T> instanceSupplier,
CheckedFunction<XContentParser, T, IOException> fromXContent) {
return xContentTester(createParser, instanceSupplier, ToXContent.EMPTY_PARAMS, fromXContent);
}
public static <T extends ToXContent> XContentTester<T> xContentTester(
CheckedBiFunction<XContent, BytesReference, XContentParser, IOException> createParser,
Supplier<T> instanceSupplier,
ToXContent.Params toXContentParams,
CheckedFunction<XContentParser, T, IOException> fromXContent) {
return new XContentTester<T>(
createParser,
instanceSupplier,
(testInstance, xContentType) ->
XContentHelper.toXContent(testInstance, xContentType, toXContentParams, false),
XContentHelper.toXContent(parsed, xContentType, toXContentParams, false),
xContentType);
fromXContent);
}
/**
* Tests converting to and from xcontent.
*/
public static class XContentTester<T> {
private final CheckedBiFunction<XContent, BytesReference, XContentParser, IOException> createParser;
private final Supplier<T> instanceSupplier;
private final CheckedBiFunction<T, XContentType, BytesReference, IOException> toXContent;
private final CheckedFunction<XContentParser, T, IOException> fromXContent;
private int numberOfTestRuns = NUMBER_OF_TEST_RUNS;
private boolean supportsUnknownFields = false;
private String[] shuffleFieldsExceptions = Strings.EMPTY_ARRAY;
private Predicate<String> randomFieldsExcludeFilter = field -> false;
private BiConsumer<T, T> assertEqualsConsumer = (expectedInstance, newInstance) -> {
assertNotSame(newInstance, expectedInstance);
assertEquals(expectedInstance, newInstance);
assertEquals(expectedInstance.hashCode(), newInstance.hashCode());
};
private boolean assertToXContentEquivalence = true;
private XContentTester(
CheckedBiFunction<XContent, BytesReference, XContentParser, IOException> createParser,
Supplier<T> instanceSupplier,
CheckedBiFunction<T, XContentType, BytesReference, IOException> toXContent,
CheckedFunction<XContentParser, T, IOException> fromXContent) {
this.createParser = createParser;
this.instanceSupplier = instanceSupplier;
this.toXContent = toXContent;
this.fromXContent = fromXContent;
}
public void test() throws IOException {
for (int runs = 0; runs < numberOfTestRuns; runs++) {
T testInstance = instanceSupplier.get();
XContentType xContentType = randomFrom(XContentType.values());
BytesReference originalXContent = toXContent.apply(testInstance, xContentType);
BytesReference shuffledContent = insertRandomFieldsAndShuffle(originalXContent, xContentType, supportsUnknownFields,
shuffleFieldsExceptions, randomFieldsExcludeFilter, createParser);
XContentParser parser = createParser.apply(XContentFactory.xContent(xContentType), shuffledContent);
T parsed = fromXContent.apply(parser);
assertEqualsConsumer.accept(testInstance, parsed);
if (assertToXContentEquivalence) {
assertToXContentEquivalent(
toXContent.apply(testInstance, xContentType),
toXContent.apply(parsed, xContentType),
xContentType);
}
}
}
public XContentTester<T> numberOfTestRuns(int numberOfTestRuns) {
this.numberOfTestRuns = numberOfTestRuns;
return this;
}
public XContentTester<T> supportsUnknownFields(boolean supportsUnknownFields) {
this.supportsUnknownFields = supportsUnknownFields;
return this;
}
public XContentTester<T> shuffleFieldsExceptions(String[] shuffleFieldsExceptions) {
this.shuffleFieldsExceptions = shuffleFieldsExceptions;
return this;
}
public XContentTester<T> randomFieldsExcludeFilter(Predicate<String> randomFieldsExcludeFilter) {
this.randomFieldsExcludeFilter = randomFieldsExcludeFilter;
return this;
}
public XContentTester<T> assertEqualsConsumer(BiConsumer<T, T> assertEqualsConsumer) {
this.assertEqualsConsumer = assertEqualsConsumer;
return this;
}
public XContentTester<T> assertToXContentEquivalence(boolean assertToXContentEquivalence) {
this.assertToXContentEquivalence = assertToXContentEquivalence;
return this;
}
}
public static <T extends ToXContent> void testFromXContent(
int numberOfTestRuns,
Supplier<T> instanceSupplier,
boolean supportsUnknownFields,
String[] shuffleFieldsExceptions,
Predicate<String> randomFieldsExcludeFilter,
CheckedBiFunction<XContent, BytesReference, XContentParser, IOException> createParserFunction,
CheckedFunction<XContentParser, T, IOException> fromXContent,
BiConsumer<T, T> assertEqualsConsumer,
boolean assertToXContentEquivalence,
ToXContent.Params toXContentParams) throws IOException {
xContentTester(createParserFunction, instanceSupplier, toXContentParams, fromXContent)
.numberOfTestRuns(numberOfTestRuns)
.supportsUnknownFields(supportsUnknownFields)
.shuffleFieldsExceptions(shuffleFieldsExceptions)
.randomFieldsExcludeFilter(randomFieldsExcludeFilter)
.assertEqualsConsumer(assertEqualsConsumer)
.assertToXContentEquivalence(assertToXContentEquivalence)
.test();
}
/**
@ -133,11 +247,9 @@ public abstract class AbstractXContentTestCase<T extends ToXContent> extends EST
return ToXContent.EMPTY_PARAMS;
}
static BytesReference insertRandomFieldsAndShuffle(ToXContent testInstance, XContentType xContentType,
static BytesReference insertRandomFieldsAndShuffle(BytesReference xContent, XContentType xContentType,
boolean supportsUnknownFields, String[] shuffleFieldsExceptions, Predicate<String> randomFieldsExcludeFilter,
CheckedBiFunction<XContent, BytesReference, XContentParser, IOException> createParserFunction,
ToXContent.Params toXContentParams) throws IOException {
BytesReference xContent = XContentHelper.toXContent(testInstance, xContentType, toXContentParams, false);
CheckedBiFunction<XContent, BytesReference, XContentParser, IOException> createParserFunction) throws IOException {
BytesReference withRandomFields;
if (supportsUnknownFields) {
// add a few random fields to check that the parser is lenient on new fields

View File

@ -22,13 +22,11 @@ package org.elasticsearch.test;
import com.carrotsearch.randomizedtesting.RandomizedContext;
import org.elasticsearch.common.bytes.BytesReference;
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;
import org.elasticsearch.common.xcontent.XContentType;
import java.io.IOException;
import java.util.Map;
import static org.hamcrest.Matchers.equalTo;
@ -37,29 +35,24 @@ import static org.hamcrest.Matchers.not;
public class AbstractXContentTestCaseTests extends ESTestCase {
public void testInsertRandomFieldsAndShuffle() throws Exception {
TestInstance t = new TestInstance();
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
builder.field("field", 1);
}
builder.endObject();
BytesReference insertRandomFieldsAndShuffle = RandomizedContext.current().runWithPrivateRandomness(1,
() -> AbstractXContentTestCase.insertRandomFieldsAndShuffle(t, XContentType.JSON, true, new String[] {}, null,
this::createParser, ToXContent.EMPTY_PARAMS));
() -> AbstractXContentTestCase.insertRandomFieldsAndShuffle(
BytesReference.bytes(builder),
XContentType.JSON,
true,
new String[] {},
null,
this::createParser));
try (XContentParser parser = createParser(XContentType.JSON.xContent(), insertRandomFieldsAndShuffle)) {
Map<String, Object> mapOrdered = parser.mapOrdered();
assertThat(mapOrdered.size(), equalTo(2));
assertThat(mapOrdered.keySet().iterator().next(), not(equalTo("field")));
}
}
private class TestInstance implements ToXContentObject {
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
{
builder.field("field", 1);
}
builder.endObject();
return builder;
}
}
}

View File

@ -148,7 +148,7 @@ public class Ccr extends Plugin implements ActionPlugin, PersistentTaskPlugin, E
@Override
public List<PersistentTasksExecutor<?>> getPersistentTasksExecutor(ClusterService clusterService,
ThreadPool threadPool, Client client) {
return Collections.singletonList(new ShardFollowTasksExecutor(settings, client, threadPool));
return Collections.singletonList(new ShardFollowTasksExecutor(settings, client, threadPool, clusterService));
}
public List<ActionHandler<? extends ActionRequest, ? extends ActionResponse>> getActions() {

View File

@ -62,6 +62,7 @@ public abstract class ShardFollowNodeTask extends AllocatedPersistentTask {
private final BiConsumer<TimeValue, Runnable> scheduler;
private final LongSupplier relativeTimeProvider;
private String followerHistoryUUID;
private long leaderGlobalCheckpoint;
private long leaderMaxSeqNo;
private long leaderMaxSeqNoOfUpdatesOrDeletes = SequenceNumbers.UNASSIGNED_SEQ_NO;
@ -110,15 +111,17 @@ public abstract class ShardFollowNodeTask extends AllocatedPersistentTask {
}
void start(
final long leaderGlobalCheckpoint,
final long leaderMaxSeqNo,
final long followerGlobalCheckpoint,
final long followerMaxSeqNo) {
final String followerHistoryUUID,
final long leaderGlobalCheckpoint,
final long leaderMaxSeqNo,
final long followerGlobalCheckpoint,
final long followerMaxSeqNo) {
/*
* While this should only ever be called once and before any other threads can touch these fields, we use synchronization here to
* avoid the need to declare these fields as volatile. That is, we are ensuring thesefields are always accessed under the same lock.
*/
synchronized (this) {
this.followerHistoryUUID = followerHistoryUUID;
this.leaderGlobalCheckpoint = leaderGlobalCheckpoint;
this.leaderMaxSeqNo = leaderMaxSeqNo;
this.followerGlobalCheckpoint = followerGlobalCheckpoint;
@ -305,7 +308,7 @@ public abstract class ShardFollowNodeTask extends AllocatedPersistentTask {
AtomicInteger retryCounter) {
assert leaderMaxSeqNoOfUpdatesOrDeletes != SequenceNumbers.UNASSIGNED_SEQ_NO : "mus is not replicated";
final long startTime = relativeTimeProvider.getAsLong();
innerSendBulkShardOperationsRequest(operations, leaderMaxSeqNoOfUpdatesOrDeletes,
innerSendBulkShardOperationsRequest(followerHistoryUUID, operations, leaderMaxSeqNoOfUpdatesOrDeletes,
response -> {
synchronized (ShardFollowNodeTask.this) {
totalIndexTimeMillis += TimeUnit.NANOSECONDS.toMillis(relativeTimeProvider.getAsLong() - startTime);
@ -404,8 +407,11 @@ public abstract class ShardFollowNodeTask extends AllocatedPersistentTask {
// These methods are protected for testing purposes:
protected abstract void innerUpdateMapping(LongConsumer handler, Consumer<Exception> errorHandler);
protected abstract void innerSendBulkShardOperationsRequest(List<Translog.Operation> operations, long leaderMaxSeqNoOfUpdatesOrDeletes,
Consumer<BulkShardOperationsResponse> handler, Consumer<Exception> errorHandler);
protected abstract void innerSendBulkShardOperationsRequest(String followerHistoryUUID,
List<Translog.Operation> operations,
long leaderMaxSeqNoOfUpdatesOrDeletes,
Consumer<BulkShardOperationsResponse> handler,
Consumer<Exception> errorHandler);
protected abstract void innerSendShardChangesRequest(long from, int maxOperationCount, Consumer<ShardChangesAction.Response> handler,
Consumer<Exception> errorHandler);

View File

@ -51,13 +51,12 @@ public class ShardFollowTask implements XPackPlugin.XPackPersistentTaskParams {
public static final ParseField MAX_WRITE_BUFFER_SIZE = new ParseField("max_write_buffer_size");
public static final ParseField MAX_RETRY_DELAY = new ParseField("max_retry_delay");
public static final ParseField POLL_TIMEOUT = new ParseField("poll_timeout");
public static final ParseField RECORDED_HISTORY_UUID = new ParseField("recorded_history_uuid");
@SuppressWarnings("unchecked")
private static ConstructingObjectParser<ShardFollowTask, Void> PARSER = new ConstructingObjectParser<>(NAME,
(a) -> new ShardFollowTask((String) a[0], new ShardId((String) a[1], (String) a[2], (int) a[3]),
new ShardId((String) a[4], (String) a[5], (int) a[6]), (int) a[7], (int) a[8], (ByteSizeValue) a[9],
(int) a[10], (int) a[11], (TimeValue) a[12], (TimeValue) a[13], (String) a[14], (Map<String, String>) a[15]));
(int) a[10], (int) a[11], (TimeValue) a[12], (TimeValue) a[13], (Map<String, String>) a[14]));
static {
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), LEADER_CLUSTER_ALIAS_FIELD);
@ -82,7 +81,6 @@ public class ShardFollowTask implements XPackPlugin.XPackPersistentTaskParams {
PARSER.declareField(ConstructingObjectParser.constructorArg(),
(p, c) -> TimeValue.parseTimeValue(p.text(), POLL_TIMEOUT.getPreferredName()),
POLL_TIMEOUT, ObjectParser.ValueType.STRING);
PARSER.declareString(ConstructingObjectParser.constructorArg(), RECORDED_HISTORY_UUID);
PARSER.declareObject(ConstructingObjectParser.constructorArg(), (p, c) -> p.mapStrings(), HEADERS);
}
@ -96,7 +94,6 @@ public class ShardFollowTask implements XPackPlugin.XPackPersistentTaskParams {
private final int maxWriteBufferSize;
private final TimeValue maxRetryDelay;
private final TimeValue pollTimeout;
private final String recordedLeaderIndexHistoryUUID;
private final Map<String, String> headers;
ShardFollowTask(
@ -110,7 +107,6 @@ public class ShardFollowTask implements XPackPlugin.XPackPersistentTaskParams {
final int maxWriteBufferSize,
final TimeValue maxRetryDelay,
final TimeValue pollTimeout,
final String recordedLeaderIndexHistoryUUID,
final Map<String, String> headers) {
this.leaderClusterAlias = leaderClusterAlias;
this.followShardId = followShardId;
@ -122,7 +118,6 @@ public class ShardFollowTask implements XPackPlugin.XPackPersistentTaskParams {
this.maxWriteBufferSize = maxWriteBufferSize;
this.maxRetryDelay = maxRetryDelay;
this.pollTimeout = pollTimeout;
this.recordedLeaderIndexHistoryUUID = recordedLeaderIndexHistoryUUID;
this.headers = headers != null ? Collections.unmodifiableMap(headers) : Collections.emptyMap();
}
@ -137,7 +132,6 @@ public class ShardFollowTask implements XPackPlugin.XPackPersistentTaskParams {
this.maxWriteBufferSize = in.readVInt();
this.maxRetryDelay = in.readTimeValue();
this.pollTimeout = in.readTimeValue();
this.recordedLeaderIndexHistoryUUID = in.readString();
this.headers = Collections.unmodifiableMap(in.readMap(StreamInput::readString, StreamInput::readString));
}
@ -185,10 +179,6 @@ public class ShardFollowTask implements XPackPlugin.XPackPersistentTaskParams {
return followShardId.getIndex().getUUID() + "-" + followShardId.getId();
}
public String getRecordedLeaderIndexHistoryUUID() {
return recordedLeaderIndexHistoryUUID;
}
public Map<String, String> getHeaders() {
return headers;
}
@ -210,7 +200,6 @@ public class ShardFollowTask implements XPackPlugin.XPackPersistentTaskParams {
out.writeVInt(maxWriteBufferSize);
out.writeTimeValue(maxRetryDelay);
out.writeTimeValue(pollTimeout);
out.writeString(recordedLeaderIndexHistoryUUID);
out.writeMap(headers, StreamOutput::writeString, StreamOutput::writeString);
}
@ -237,7 +226,6 @@ public class ShardFollowTask implements XPackPlugin.XPackPersistentTaskParams {
builder.field(MAX_WRITE_BUFFER_SIZE.getPreferredName(), maxWriteBufferSize);
builder.field(MAX_RETRY_DELAY.getPreferredName(), maxRetryDelay.getStringRep());
builder.field(POLL_TIMEOUT.getPreferredName(), pollTimeout.getStringRep());
builder.field(RECORDED_HISTORY_UUID.getPreferredName(), recordedLeaderIndexHistoryUUID);
builder.field(HEADERS.getPreferredName(), headers);
return builder.endObject();
}
@ -257,7 +245,6 @@ public class ShardFollowTask implements XPackPlugin.XPackPersistentTaskParams {
maxWriteBufferSize == that.maxWriteBufferSize &&
Objects.equals(maxRetryDelay, that.maxRetryDelay) &&
Objects.equals(pollTimeout, that.pollTimeout) &&
Objects.equals(recordedLeaderIndexHistoryUUID, that.recordedLeaderIndexHistoryUUID) &&
Objects.equals(headers, that.headers);
}
@ -274,8 +261,8 @@ public class ShardFollowTask implements XPackPlugin.XPackPersistentTaskParams {
maxWriteBufferSize,
maxRetryDelay,
pollTimeout,
recordedLeaderIndexHistoryUUID,
headers);
headers
);
}
public String toString() {

View File

@ -17,12 +17,15 @@ import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.cluster.routing.IndexRoutingTable;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.engine.CommitStats;
import org.elasticsearch.index.engine.Engine;
import org.elasticsearch.index.seqno.SeqNoStats;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.shard.ShardNotFoundException;
@ -47,16 +50,19 @@ import java.util.function.Consumer;
import java.util.function.LongConsumer;
import static org.elasticsearch.xpack.ccr.CcrLicenseChecker.wrapClient;
import static org.elasticsearch.xpack.ccr.action.TransportResumeFollowAction.extractLeaderShardHistoryUUIDs;
public class ShardFollowTasksExecutor extends PersistentTasksExecutor<ShardFollowTask> {
private final Client client;
private final ThreadPool threadPool;
private final ClusterService clusterService;
public ShardFollowTasksExecutor(Settings settings, Client client, ThreadPool threadPool) {
public ShardFollowTasksExecutor(Settings settings, Client client, ThreadPool threadPool, ClusterService clusterService) {
super(settings, ShardFollowTask.NAME, Ccr.CCR_THREAD_POOL_NAME);
this.client = client;
this.threadPool = threadPool;
this.clusterService = clusterService;
}
@Override
@ -99,8 +105,10 @@ public class ShardFollowTasksExecutor extends PersistentTasksExecutor<ShardFollo
}
}
};
return new ShardFollowNodeTask(
id, type, action, getDescription(taskInProgress), parentTaskId, headers, params, scheduler, System::nanoTime) {
final String recordedLeaderShardHistoryUUID = getLeaderShardHistoryUUID(params);
return new ShardFollowNodeTask(id, type, action, getDescription(taskInProgress), parentTaskId, headers, params,
scheduler, System::nanoTime) {
@Override
protected void innerUpdateMapping(LongConsumer handler, Consumer<Exception> errorHandler) {
@ -135,12 +143,14 @@ public class ShardFollowTasksExecutor extends PersistentTasksExecutor<ShardFollo
@Override
protected void innerSendBulkShardOperationsRequest(
final List<Translog.Operation> operations,
final long maxSeqNoOfUpdatesOrDeletes,
final Consumer<BulkShardOperationsResponse> handler,
final Consumer<Exception> errorHandler) {
final BulkShardOperationsRequest request = new BulkShardOperationsRequest(
params.getFollowShardId(), operations, maxSeqNoOfUpdatesOrDeletes);
final String followerHistoryUUID,
final List<Translog.Operation> operations,
final long maxSeqNoOfUpdatesOrDeletes,
final Consumer<BulkShardOperationsResponse> handler,
final Consumer<Exception> errorHandler) {
final BulkShardOperationsRequest request = new BulkShardOperationsRequest(params.getFollowShardId(),
followerHistoryUUID, operations, maxSeqNoOfUpdatesOrDeletes);
followerClient.execute(BulkShardOperationsAction.INSTANCE, request,
ActionListener.wrap(response -> handler.accept(response), errorHandler));
}
@ -149,7 +159,7 @@ public class ShardFollowTasksExecutor extends PersistentTasksExecutor<ShardFollo
protected void innerSendShardChangesRequest(long from, int maxOperationCount, Consumer<ShardChangesAction.Response> handler,
Consumer<Exception> errorHandler) {
ShardChangesAction.Request request =
new ShardChangesAction.Request(params.getLeaderShardId(), params.getRecordedLeaderIndexHistoryUUID());
new ShardChangesAction.Request(params.getLeaderShardId(), recordedLeaderShardHistoryUUID);
request.setFromSeqNo(from);
request.setMaxOperationCount(maxOperationCount);
request.setMaxBatchSize(params.getMaxBatchSize());
@ -159,8 +169,15 @@ public class ShardFollowTasksExecutor extends PersistentTasksExecutor<ShardFollo
};
}
interface BiLongConsumer {
void accept(long x, long y);
private String getLeaderShardHistoryUUID(ShardFollowTask params) {
IndexMetaData followIndexMetaData = clusterService.state().metaData().index(params.getFollowShardId().getIndex());
Map<String, String> ccrIndexMetadata = followIndexMetaData.getCustomData(Ccr.CCR_CUSTOM_METADATA_KEY);
String[] recordedLeaderShardHistoryUUIDs = extractLeaderShardHistoryUUIDs(ccrIndexMetadata);
return recordedLeaderShardHistoryUUIDs[params.getLeaderShardId().id()];
}
interface FollowerStatsInfoHandler {
void accept(String followerHistoryUUID, long globalCheckpoint, long maxSeqNo);
}
@Override
@ -169,7 +186,9 @@ public class ShardFollowTasksExecutor extends PersistentTasksExecutor<ShardFollo
ShardFollowNodeTask shardFollowNodeTask = (ShardFollowNodeTask) task;
logger.info("{} Starting to track leader shard {}", params.getFollowShardId(), params.getLeaderShardId());
BiLongConsumer handler = (followerGCP, maxSeqNo) -> shardFollowNodeTask.start(followerGCP, maxSeqNo, followerGCP, maxSeqNo);
FollowerStatsInfoHandler handler = (followerHistoryUUID, followerGCP, maxSeqNo) -> {
shardFollowNodeTask.start(followerHistoryUUID, followerGCP, maxSeqNo, followerGCP, maxSeqNo);
};
Consumer<Exception> errorHandler = e -> {
if (shardFollowNodeTask.isStopped()) {
return;
@ -184,13 +203,13 @@ public class ShardFollowTasksExecutor extends PersistentTasksExecutor<ShardFollo
}
};
fetchGlobalCheckpoint(followerClient, params.getFollowShardId(), handler, errorHandler);
fetchFollowerShardInfo(followerClient, params.getFollowShardId(), handler, errorHandler);
}
private void fetchGlobalCheckpoint(
private void fetchFollowerShardInfo(
final Client client,
final ShardId shardId,
final BiLongConsumer handler,
final FollowerStatsInfoHandler handler,
final Consumer<Exception> errorHandler) {
client.admin().indices().stats(new IndicesStatsRequest().indices(shardId.getIndexName()), ActionListener.wrap(r -> {
IndexStats indexStats = r.getIndex(shardId.getIndexName());
@ -204,10 +223,14 @@ public class ShardFollowTasksExecutor extends PersistentTasksExecutor<ShardFollo
.filter(shardStats -> shardStats.getShardRouting().primary())
.findAny();
if (filteredShardStats.isPresent()) {
final SeqNoStats seqNoStats = filteredShardStats.get().getSeqNoStats();
final ShardStats shardStats = filteredShardStats.get();
final CommitStats commitStats = shardStats.getCommitStats();
final String historyUUID = commitStats.getUserData().get(Engine.HISTORY_UUID_KEY);
final SeqNoStats seqNoStats = shardStats.getSeqNoStats();
final long globalCheckpoint = seqNoStats.getGlobalCheckpoint();
final long maxSeqNo = seqNoStats.getMaxSeqNo();
handler.accept(globalCheckpoint, maxSeqNo);
handler.accept(historyUUID, globalCheckpoint, maxSeqNo);
} else {
errorHandler.accept(new ShardNotFoundException(shardId));
}

View File

@ -174,7 +174,7 @@ public final class TransportPutFollowAction
listener::onFailure);
// Can't use create index api here, because then index templates can alter the mappings / settings.
// And index templates could introduce settings / mappings that are incompatible with the leader index.
clusterService.submitStateUpdateTask("follow_index_action", new AckedClusterStateUpdateTask<Boolean>(request, handler) {
clusterService.submitStateUpdateTask("create_following_index", new AckedClusterStateUpdateTask<Boolean>(request, handler) {
@Override
protected Boolean newResponse(final boolean acknowledged) {

View File

@ -192,12 +192,9 @@ public class TransportResumeFollowAction extends HandledTransportAction<ResumeFo
for (int i = 0; i < numShards; i++) {
final int shardId = i;
String taskId = followIndexMetadata.getIndexUUID() + "-" + shardId;
Map<String, String> ccrIndexMetadata = followIndexMetadata.getCustomData(Ccr.CCR_CUSTOM_METADATA_KEY);
String[] recordedLeaderShardHistoryUUIDs = extractIndexShardHistoryUUIDs(ccrIndexMetadata);
String recordedLeaderShardHistoryUUID = recordedLeaderShardHistoryUUIDs[shardId];
final ShardFollowTask shardFollowTask = createShardFollowTask(shardId, clusterNameAlias, request,
leaderIndexMetadata, followIndexMetadata, recordedLeaderShardHistoryUUID, filteredHeaders);
final ShardFollowTask shardFollowTask = createShardFollowTask(shardId, clusterNameAlias, request,
leaderIndexMetadata, followIndexMetadata, filteredHeaders);
persistentTasksService.sendStartRequest(taskId, ShardFollowTask.NAME, shardFollowTask,
new ActionListener<PersistentTasksCustomMetaData.PersistentTask<ShardFollowTask>>() {
@Override
@ -263,7 +260,7 @@ public class TransportResumeFollowAction extends HandledTransportAction<ResumeFo
"] as leader index but instead reference [" + recordedLeaderIndexUUID + "] as leader index");
}
String[] recordedHistoryUUIDs = extractIndexShardHistoryUUIDs(ccrIndexMetadata);
String[] recordedHistoryUUIDs = extractLeaderShardHistoryUUIDs(ccrIndexMetadata);
assert recordedHistoryUUIDs.length == leaderIndexHistoryUUID.length;
for (int i = 0; i < leaderIndexHistoryUUID.length; i++) {
String recordedLeaderIndexHistoryUUID = recordedHistoryUUIDs[i];
@ -311,7 +308,6 @@ public class TransportResumeFollowAction extends HandledTransportAction<ResumeFo
ResumeFollowAction.Request request,
IndexMetaData leaderIndexMetadata,
IndexMetaData followIndexMetadata,
String recordedLeaderShardHistoryUUID,
Map<String, String> filteredHeaders
) {
int maxBatchOperationCount;
@ -363,13 +359,16 @@ public class TransportResumeFollowAction extends HandledTransportAction<ResumeFo
maxWriteBufferSize,
maxRetryDelay,
pollTimeout,
recordedLeaderShardHistoryUUID,
filteredHeaders
);
}
private static String[] extractIndexShardHistoryUUIDs(Map<String, String> ccrIndexMetaData) {
static String[] extractLeaderShardHistoryUUIDs(Map<String, String> ccrIndexMetaData) {
String historyUUIDs = ccrIndexMetaData.get(Ccr.CCR_CUSTOM_METADATA_LEADER_INDEX_SHARD_HISTORY_UUIDS);
if (historyUUIDs == null) {
throw new IllegalArgumentException("leader index shard UUIDs are missing");
}
return historyUUIDs.split(",");
}

View File

@ -16,19 +16,28 @@ import java.util.List;
public final class BulkShardOperationsRequest extends ReplicatedWriteRequest<BulkShardOperationsRequest> {
private String historyUUID;
private List<Translog.Operation> operations;
private long maxSeqNoOfUpdatesOrDeletes;
public BulkShardOperationsRequest() {
}
public BulkShardOperationsRequest(ShardId shardId, List<Translog.Operation> operations, long maxSeqNoOfUpdatesOrDeletes) {
public BulkShardOperationsRequest(final ShardId shardId,
final String historyUUID,
final List<Translog.Operation> operations,
long maxSeqNoOfUpdatesOrDeletes) {
super(shardId);
setRefreshPolicy(RefreshPolicy.NONE);
this.historyUUID = historyUUID;
this.operations = operations;
this.maxSeqNoOfUpdatesOrDeletes = maxSeqNoOfUpdatesOrDeletes;
}
public String getHistoryUUID() {
return historyUUID;
}
public List<Translog.Operation> getOperations() {
return operations;
}
@ -40,6 +49,7 @@ public final class BulkShardOperationsRequest extends ReplicatedWriteRequest<Bul
@Override
public void readFrom(final StreamInput in) throws IOException {
super.readFrom(in);
historyUUID = in.readString();
maxSeqNoOfUpdatesOrDeletes = in.readZLong();
operations = in.readList(Translog.Operation::readOperation);
}
@ -47,6 +57,7 @@ public final class BulkShardOperationsRequest extends ReplicatedWriteRequest<Bul
@Override
public void writeTo(final StreamOutput out) throws IOException {
super.writeTo(out);
out.writeString(historyUUID);
out.writeZLong(maxSeqNoOfUpdatesOrDeletes);
out.writeVInt(operations.size());
for (Translog.Operation operation : operations) {
@ -57,7 +68,8 @@ public final class BulkShardOperationsRequest extends ReplicatedWriteRequest<Bul
@Override
public String toString() {
return "BulkShardOperationsRequest{" +
"operations=" + operations.size()+
"historyUUID=" + historyUUID +
", operations=" + operations.size() +
", maxSeqNoUpdates=" + maxSeqNoOfUpdatesOrDeletes +
", shardId=" + shardId +
", timeout=" + timeout +

View File

@ -61,17 +61,23 @@ public class TransportBulkShardOperationsAction
@Override
protected WritePrimaryResult<BulkShardOperationsRequest, BulkShardOperationsResponse> shardOperationOnPrimary(
final BulkShardOperationsRequest request, final IndexShard primary) throws Exception {
return shardOperationOnPrimary(
request.shardId(), request.getOperations(), request.getMaxSeqNoOfUpdatesOrDeletes(), primary, logger);
return shardOperationOnPrimary(request.shardId(), request.getHistoryUUID(), request.getOperations(),
request.getMaxSeqNoOfUpdatesOrDeletes(), primary, logger);
}
// public for testing purposes only
public static WritePrimaryResult<BulkShardOperationsRequest, BulkShardOperationsResponse> shardOperationOnPrimary(
final ShardId shardId,
final String historyUUID,
final List<Translog.Operation> sourceOperations,
final long maxSeqNoOfUpdatesOrDeletes,
final IndexShard primary,
final Logger logger) throws IOException {
if (historyUUID.equalsIgnoreCase(primary.getHistoryUUID()) == false) {
throw new IllegalStateException("unexpected history uuid, expected [" + historyUUID +
"], actual [" + primary.getHistoryUUID() + "], shard is likely restored from snapshot or force allocated");
}
final List<Translog.Operation> targetOperations = sourceOperations.stream().map(operation -> {
final Translog.Operation operationWithPrimaryTerm;
switch (operation.opType()) {
@ -110,7 +116,7 @@ public class TransportBulkShardOperationsAction
primary.advanceMaxSeqNoOfUpdatesOrDeletes(maxSeqNoOfUpdatesOrDeletes);
final Translog.Location location = applyTranslogOperations(targetOperations, primary, Engine.Operation.Origin.PRIMARY);
final BulkShardOperationsRequest replicaRequest = new BulkShardOperationsRequest(
shardId, targetOperations, maxSeqNoOfUpdatesOrDeletes);
shardId, historyUUID, targetOperations, maxSeqNoOfUpdatesOrDeletes);
return new CcrWritePrimaryResult(replicaRequest, location, primary, logger);
}

View File

@ -51,7 +51,7 @@ public class ShardFollowNodeTaskRandomTests extends ESTestCase {
}
private void startAndAssertAndStopTask(ShardFollowNodeTask task, TestRun testRun) throws Exception {
task.start(testRun.startSeqNo - 1, testRun.startSeqNo - 1, testRun.startSeqNo - 1, testRun.startSeqNo - 1);
task.start("uuid", testRun.startSeqNo - 1, testRun.startSeqNo - 1, testRun.startSeqNo - 1, testRun.startSeqNo - 1);
assertBusy(() -> {
ShardFollowNodeTaskStatus status = task.getStatus();
assertThat(status.leaderGlobalCheckpoint(), equalTo(testRun.finalExpectedGlobalCheckpoint));
@ -85,7 +85,6 @@ public class ShardFollowNodeTaskRandomTests extends ESTestCase {
10240,
TimeValue.timeValueMillis(10),
TimeValue.timeValueMillis(10),
"uuid",
Collections.emptyMap()
);
@ -111,10 +110,10 @@ public class ShardFollowNodeTaskRandomTests extends ESTestCase {
@Override
protected void innerSendBulkShardOperationsRequest(
List<Translog.Operation> operations,
long maxSeqNoOfUpdates,
Consumer<BulkShardOperationsResponse> handler,
Consumer<Exception> errorHandler) {
String followerHistoryUUID, List<Translog.Operation> operations,
long maxSeqNoOfUpdates,
Consumer<BulkShardOperationsResponse> handler,
Consumer<Exception> errorHandler) {
for(Translog.Operation op : operations) {
tracker.markSeqNoAsCompleted(op.seqNo());
}

View File

@ -125,7 +125,7 @@ public class ShardFollowNodeTaskTests extends ESTestCase {
shardChangesRequests.clear();
// The call the updateMapping is a noop, so noting happens.
task.start(128L, 128L, task.getStatus().followerGlobalCheckpoint(), task.getStatus().followerMaxSeqNo());
task.start("uuid", 128L, 128L, task.getStatus().followerGlobalCheckpoint(), task.getStatus().followerMaxSeqNo());
task.markAsCompleted();
task.coordinateReads();
assertThat(shardChangesRequests.size(), equalTo(0));
@ -682,7 +682,6 @@ public class ShardFollowNodeTaskTests extends ESTestCase {
bufferWriteLimit,
TimeValue.ZERO,
TimeValue.ZERO,
"uuid",
Collections.emptyMap()
);
@ -715,10 +714,10 @@ public class ShardFollowNodeTaskTests extends ESTestCase {
@Override
protected void innerSendBulkShardOperationsRequest(
final List<Translog.Operation> operations,
final long maxSeqNoOfUpdates,
final Consumer<BulkShardOperationsResponse> handler,
final Consumer<Exception> errorHandler) {
String followerHistoryUUID, final List<Translog.Operation> operations,
final long maxSeqNoOfUpdates,
final Consumer<BulkShardOperationsResponse> handler,
final Consumer<Exception> errorHandler) {
bulkShardOperationRequests.add(operations);
Exception writeFailure = ShardFollowNodeTaskTests.this.writeFailures.poll();
if (writeFailure != null) {
@ -796,7 +795,7 @@ public class ShardFollowNodeTaskTests extends ESTestCase {
void startTask(ShardFollowNodeTask task, long leaderGlobalCheckpoint, long followerGlobalCheckpoint) {
// The call the updateMapping is a noop, so noting happens.
task.start(leaderGlobalCheckpoint, leaderGlobalCheckpoint, followerGlobalCheckpoint, followerGlobalCheckpoint);
task.start("uuid", leaderGlobalCheckpoint, leaderGlobalCheckpoint, followerGlobalCheckpoint, followerGlobalCheckpoint);
}

View File

@ -63,6 +63,7 @@ public class ShardFollowTaskReplicationTests extends ESIndexLevelReplicationTest
final SeqNoStats leaderSeqNoStats = leaderGroup.getPrimary().seqNoStats();
final SeqNoStats followerSeqNoStats = followerGroup.getPrimary().seqNoStats();
shardFollowTask.start(
followerGroup.getPrimary().getHistoryUUID(),
leaderSeqNoStats.getGlobalCheckpoint(),
leaderSeqNoStats.getMaxSeqNo(),
followerSeqNoStats.getGlobalCheckpoint(),
@ -103,6 +104,7 @@ public class ShardFollowTaskReplicationTests extends ESIndexLevelReplicationTest
final SeqNoStats leaderSeqNoStats = leaderGroup.getPrimary().seqNoStats();
final SeqNoStats followerSeqNoStats = followerGroup.getPrimary().seqNoStats();
shardFollowTask.start(
followerGroup.getPrimary().getHistoryUUID(),
leaderSeqNoStats.getGlobalCheckpoint(),
leaderSeqNoStats.getMaxSeqNo(),
followerSeqNoStats.getGlobalCheckpoint(),
@ -137,7 +139,7 @@ public class ShardFollowTaskReplicationTests extends ESIndexLevelReplicationTest
}
}
public void testChangeHistoryUUID() throws Exception {
public void testChangeLeaderHistoryUUID() throws Exception {
try (ReplicationGroup leaderGroup = createGroup(0);
ReplicationGroup followerGroup = createFollowGroup(0)) {
leaderGroup.startAll();
@ -148,6 +150,7 @@ public class ShardFollowTaskReplicationTests extends ESIndexLevelReplicationTest
final SeqNoStats leaderSeqNoStats = leaderGroup.getPrimary().seqNoStats();
final SeqNoStats followerSeqNoStats = followerGroup.getPrimary().seqNoStats();
shardFollowTask.start(
followerGroup.getPrimary().getHistoryUUID(),
leaderSeqNoStats.getGlobalCheckpoint(),
leaderSeqNoStats.getMaxSeqNo(),
followerSeqNoStats.getGlobalCheckpoint(),
@ -177,6 +180,47 @@ public class ShardFollowTaskReplicationTests extends ESIndexLevelReplicationTest
}
}
public void testChangeFollowerHistoryUUID() throws Exception {
try (ReplicationGroup leaderGroup = createGroup(0);
ReplicationGroup followerGroup = createFollowGroup(0)) {
leaderGroup.startAll();
int docCount = leaderGroup.appendDocs(randomInt(64));
leaderGroup.assertAllEqual(docCount);
followerGroup.startAll();
ShardFollowNodeTask shardFollowTask = createShardFollowTask(leaderGroup, followerGroup);
final SeqNoStats leaderSeqNoStats = leaderGroup.getPrimary().seqNoStats();
final SeqNoStats followerSeqNoStats = followerGroup.getPrimary().seqNoStats();
shardFollowTask.start(
followerGroup.getPrimary().getHistoryUUID(),
leaderSeqNoStats.getGlobalCheckpoint(),
leaderSeqNoStats.getMaxSeqNo(),
followerSeqNoStats.getGlobalCheckpoint(),
followerSeqNoStats.getMaxSeqNo());
leaderGroup.syncGlobalCheckpoint();
leaderGroup.assertAllEqual(docCount);
Set<String> indexedDocIds = getShardDocUIDs(leaderGroup.getPrimary());
assertBusy(() -> {
assertThat(followerGroup.getPrimary().getGlobalCheckpoint(), equalTo(leaderGroup.getPrimary().getGlobalCheckpoint()));
followerGroup.assertAllEqual(indexedDocIds.size());
});
String oldHistoryUUID = followerGroup.getPrimary().getHistoryUUID();
followerGroup.reinitPrimaryShard();
followerGroup.getPrimary().store().bootstrapNewHistory();
recoverShardFromStore(followerGroup.getPrimary());
String newHistoryUUID = followerGroup.getPrimary().getHistoryUUID();
// force the global checkpoint on the leader to advance
leaderGroup.appendDocs(64);
assertBusy(() -> {
assertThat(shardFollowTask.isStopped(), is(true));
assertThat(shardFollowTask.getFailure().getMessage(), equalTo("unexpected history uuid, expected [" + oldHistoryUUID +
"], actual [" + newHistoryUUID + "], shard is likely restored from snapshot or force allocated"));
});
}
}
@Override
protected ReplicationGroup createGroup(int replicas, Settings settings) throws IOException {
Settings newSettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
@ -217,9 +261,9 @@ public class ShardFollowTaskReplicationTests extends ESIndexLevelReplicationTest
between(1, 4), 10240,
TimeValue.timeValueMillis(10),
TimeValue.timeValueMillis(10),
leaderGroup.getPrimary().getHistoryUUID(),
Collections.emptyMap()
);
final String recordedLeaderIndexHistoryUUID = leaderGroup.getPrimary().getHistoryUUID();
BiConsumer<TimeValue, Runnable> scheduler = (delay, task) -> threadPool.schedule(delay, ThreadPool.Names.GENERIC, task);
AtomicBoolean stopped = new AtomicBoolean(false);
@ -245,13 +289,14 @@ public class ShardFollowTaskReplicationTests extends ESIndexLevelReplicationTest
@Override
protected void innerSendBulkShardOperationsRequest(
final List<Translog.Operation> operations,
final long maxSeqNoOfUpdates,
final Consumer<BulkShardOperationsResponse> handler,
final Consumer<Exception> errorHandler) {
final String followerHistoryUUID,
final List<Translog.Operation> operations,
final long maxSeqNoOfUpdates,
final Consumer<BulkShardOperationsResponse> handler,
final Consumer<Exception> errorHandler) {
Runnable task = () -> {
BulkShardOperationsRequest request = new BulkShardOperationsRequest(
params.getFollowShardId(), operations, maxSeqNoOfUpdates);
BulkShardOperationsRequest request = new BulkShardOperationsRequest(params.getFollowShardId(),
followerHistoryUUID, operations, maxSeqNoOfUpdates);
ActionListener<BulkShardOperationsResponse> listener = ActionListener.wrap(handler::accept, errorHandler);
new CCRAction(request, listener, followerGroup).execute();
};
@ -277,7 +322,7 @@ public class ShardFollowTaskReplicationTests extends ESIndexLevelReplicationTest
return;
}
Translog.Operation[] ops = ShardChangesAction.getOperations(indexShard, seqNoStats.getGlobalCheckpoint(), from,
maxOperationCount, params.getRecordedLeaderIndexHistoryUUID(), params.getMaxBatchSize());
maxOperationCount, recordedLeaderIndexHistoryUUID, params.getMaxBatchSize());
// hard code mapping version; this is ok, as mapping updates are not tested here
final ShardChangesAction.Response response = new ShardChangesAction.Response(
1L,
@ -340,8 +385,8 @@ public class ShardFollowTaskReplicationTests extends ESIndexLevelReplicationTest
@Override
protected PrimaryResult performOnPrimary(IndexShard primary, BulkShardOperationsRequest request) throws Exception {
TransportWriteAction.WritePrimaryResult<BulkShardOperationsRequest, BulkShardOperationsResponse> result =
TransportBulkShardOperationsAction.shardOperationOnPrimary(primary.shardId(), request.getOperations(),
request.getMaxSeqNoOfUpdatesOrDeletes(), primary, logger);
TransportBulkShardOperationsAction.shardOperationOnPrimary(primary.shardId(), request.getHistoryUUID(),
request.getOperations(), request.getMaxSeqNoOfUpdatesOrDeletes(), primary, logger);
return new PrimaryResult(result.replicaRequest(), result.finalResponseIfSuccessful);
}

View File

@ -36,7 +36,6 @@ public class ShardFollowTaskTests extends AbstractSerializingTestCase<ShardFollo
randomIntBetween(1, Integer.MAX_VALUE),
TimeValue.parseTimeValue(randomTimeValue(), ""),
TimeValue.parseTimeValue(randomTimeValue(), ""),
randomAlphaOfLength(4),
randomBoolean() ? null : Collections.singletonMap("key", "value")
);
}

View File

@ -83,7 +83,6 @@ public class TransportUnfollowActionTests extends ESTestCase {
10240,
TimeValue.timeValueMillis(10),
TimeValue.timeValueMillis(10),
"uuid",
Collections.emptyMap()
);
PersistentTasksCustomMetaData.PersistentTask<?> task =

View File

@ -59,7 +59,8 @@ public class BulkShardOperationsTests extends IndexShardTestCase {
}
final TransportWriteAction.WritePrimaryResult<BulkShardOperationsRequest, BulkShardOperationsResponse> result =
TransportBulkShardOperationsAction.shardOperationOnPrimary(followerPrimary.shardId(), operations,
TransportBulkShardOperationsAction.shardOperationOnPrimary(followerPrimary.shardId(), followerPrimary.getHistoryUUID(),
operations,
numOps - 1, followerPrimary, logger);
try (Translog.Snapshot snapshot = followerPrimary.getHistoryOperations("test", 0)) {

View File

@ -222,7 +222,7 @@ public class RollupJobConfig implements NamedWriteable, ToXContentObject {
builder.endArray();
}
if (timeout != null) {
builder.field(TIMEOUT, timeout);
builder.field(TIMEOUT, timeout.getStringRep());
}
builder.field(PAGE_SIZE, pageSize);
}

View File

@ -163,6 +163,6 @@ public class RollupJobStatus implements Task.Status, PersistentTaskState {
@Override
public int hashCode() {
return Objects.hash(state, currentPosition, upgradedDocumentID);
return Objects.hash(state, currentPosition, upgradedDocumentID);
}
}

View File

@ -477,9 +477,7 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
try (InputStream is = Files.newInputStream(keyStorePath)) {
keyStore.load(is, keyStorePass.toCharArray());
}
// TODO Revisit TLS1.2 pinning when TLS1.3 is fully supported
// https://github.com/elastic/elasticsearch/issues/32276
final SSLContext sslContext = new SSLContextBuilder().useProtocol("TLSv1.2").loadKeyMaterial(keyStore, keyStorePass.toCharArray())
final SSLContext sslContext = new SSLContextBuilder().loadKeyMaterial(keyStore, keyStorePass.toCharArray())
.build();
MockWebServer server = new MockWebServer(sslContext, false);
server.enqueue(new MockResponse().setResponseCode(200).setBody("body"));
@ -493,9 +491,7 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
keyStore.load(null, password.toCharArray());
keyStore.setKeyEntry("testnode_ec", PemUtils.readPrivateKey(keyPath, password::toCharArray), password.toCharArray(),
CertParsingUtils.readCertificates(Collections.singletonList(certPath)));
// TODO Revisit TLS1.2 pinning when TLS1.3 is fully supported
// https://github.com/elastic/elasticsearch/issues/32276
final SSLContext sslContext = new SSLContextBuilder().useProtocol("TLSv1.2").loadKeyMaterial(keyStore, password.toCharArray())
final SSLContext sslContext = new SSLContextBuilder().loadKeyMaterial(keyStore, password.toCharArray())
.build();
MockWebServer server = new MockWebServer(sslContext, false);
server.enqueue(new MockResponse().setResponseCode(200).setBody("body"));
@ -510,7 +506,7 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
try (InputStream is = Files.newInputStream(trustStorePath)) {
trustStore.load(is, trustStorePass.toCharArray());
}
final SSLContext sslContext = new SSLContextBuilder().useProtocol("TLSv1.2").loadTrustMaterial(trustStore, null).build();
final SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(trustStore, null).build();
return HttpClients.custom().setSSLContext(sslContext).build();
}
@ -527,7 +523,7 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
for (Certificate cert : CertParsingUtils.readCertificates(trustedCertificatePaths)) {
trustStore.setCertificateEntry(cert.toString(), cert);
}
final SSLContext sslContext = new SSLContextBuilder().useProtocol("TLSv1.2").loadTrustMaterial(trustStore, null).build();
final SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(trustStore, null).build();
return HttpClients.custom().setSSLContext(sslContext).build();
}

View File

@ -314,7 +314,7 @@ public class MonitoringIT extends ESSingleNodeTestCase {
private void assertClusterStatsMonitoringDoc(final Map<String, Object> document,
final boolean apmIndicesExist) {
final Map<String, Object> source = (Map<String, Object>) document.get("_source");
assertEquals(11, source.size());
assertEquals(12, source.size());
assertThat((String) source.get("cluster_name"), not(isEmptyOrNullString()));
assertThat(source.get("version"), equalTo(Version.CURRENT.toString()));

View File

@ -8,6 +8,7 @@ package org.elasticsearch.xpack.rollup.rest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestController;
@ -27,7 +28,8 @@ public class RestGetRollupIndexCapsAction extends BaseRestHandler {
protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) {
String index = restRequest.param(INDEX.getPreferredName());
IndicesOptions options = IndicesOptions.fromRequest(restRequest, IndicesOptions.STRICT_EXPAND_OPEN_FORBID_CLOSED);
GetRollupIndexCapsAction.Request request = new GetRollupIndexCapsAction.Request(new String[]{index}, options);
GetRollupIndexCapsAction.Request request =
new GetRollupIndexCapsAction.Request(Strings.splitStringByCommaToArray(index), options);
return channel -> client.execute(GetRollupIndexCapsAction.INSTANCE, request, new RestToXContentListener<>(channel));
}

View File

@ -307,8 +307,8 @@ unquoteIdentifier
;
number
: (PLUS | MINUS)? DECIMAL_VALUE #decimalLiteral
| (PLUS | MINUS)? INTEGER_VALUE #integerLiteral
: DECIMAL_VALUE #decimalLiteral
| INTEGER_VALUE #integerLiteral
;
string
@ -454,7 +454,7 @@ DIGIT_IDENTIFIER
;
TABLE_IDENTIFIER
: (LETTER | DIGIT | '_' | '@' | ASTERISK)+
: (LETTER | DIGIT | '_')+
;
QUOTED_IDENTIFIER

View File

@ -771,9 +771,9 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
return uf;
}
String normalizedName = functionRegistry.concreteFunctionName(name);
String functionName = functionRegistry.resolveAlias(name);
List<Function> list = getList(seen, normalizedName);
List<Function> list = getList(seen, functionName);
// first try to resolve from seen functions
if (!list.isEmpty()) {
for (Function seenFunction : list) {
@ -784,11 +784,11 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
}
// not seen before, use the registry
if (!functionRegistry.functionExists(name)) {
return uf.missing(normalizedName, functionRegistry.listFunctions());
if (!functionRegistry.functionExists(functionName)) {
return uf.missing(functionName, functionRegistry.listFunctions());
}
// TODO: look into Generator for significant terms, etc..
FunctionDefinition def = functionRegistry.resolveFunction(normalizedName);
FunctionDefinition def = functionRegistry.resolveFunction(functionName);
Function f = uf.buildResolved(timeZone, def);
list.add(f);

View File

@ -90,6 +90,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.function.BiFunction;
@ -211,21 +212,23 @@ public class FunctionRegistry {
}
}
public FunctionDefinition resolveFunction(String name) {
FunctionDefinition def = defs.get(normalize(name));
public FunctionDefinition resolveFunction(String functionName) {
FunctionDefinition def = defs.get(functionName);
if (def == null) {
throw new SqlIllegalArgumentException("Cannot find function {}; this should have been caught during analysis", name);
throw new SqlIllegalArgumentException(
"Cannot find function {}; this should have been caught during analysis",
functionName);
}
return def;
}
public String concreteFunctionName(String alias) {
String normalized = normalize(alias);
return aliases.getOrDefault(normalized, normalized);
public String resolveAlias(String alias) {
String upperCase = alias.toUpperCase(Locale.ROOT);
return aliases.getOrDefault(upperCase, upperCase);
}
public boolean functionExists(String name) {
return defs.containsKey(normalize(name));
public boolean functionExists(String functionName) {
return defs.containsKey(functionName);
}
public Collection<FunctionDefinition> listFunctions() {

View File

@ -286,6 +286,9 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
case SqlBaseParser.PLUS:
return value;
case SqlBaseParser.MINUS:
if (value instanceof Literal) { // Minus already processed together with literal number
return value;
}
return new Neg(source(ctx.operator), value);
default:
throw new ParsingException(loc, "Unknown arithemtic {}", ctx.operator.getText());
@ -483,38 +486,40 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
@Override
public Literal visitDecimalLiteral(DecimalLiteralContext ctx) {
String ctxText = (hasMinusFromParent(ctx) ? "-" : "") + ctx.getText();
double value;
try {
value = Double.parseDouble(ctx.getText());
value = Double.parseDouble(ctxText);
} catch (NumberFormatException nfe) {
throw new ParsingException(source(ctx), "Cannot parse number [{}]", ctx.getText());
throw new ParsingException(source(ctx), "Cannot parse number [{}]", ctxText);
}
if (Double.isInfinite(value)) {
throw new ParsingException(source(ctx), "Number [{}] is too large", ctx.getText());
throw new ParsingException(source(ctx), "Number [{}] is too large", ctxText);
}
if (Double.isNaN(value)) {
throw new ParsingException(source(ctx), "[{}] cannot be parsed as a number (NaN)", ctx.getText());
throw new ParsingException(source(ctx), "[{}] cannot be parsed as a number (NaN)", ctxText);
}
return new Literal(source(ctx), Double.valueOf(value), DataType.DOUBLE);
}
@Override
public Literal visitIntegerLiteral(IntegerLiteralContext ctx) {
String ctxText = (hasMinusFromParent(ctx) ? "-" : "") + ctx.getText();
long value;
try {
value = Long.parseLong(ctx.getText());
value = Long.parseLong(ctxText);
} catch (NumberFormatException nfe) {
try {
BigInteger bi = new BigInteger(ctx.getText());
BigInteger bi = new BigInteger(ctxText);
try {
bi.longValueExact();
} catch (ArithmeticException ae) {
throw new ParsingException(source(ctx), "Number [{}] is too large", ctx.getText());
throw new ParsingException(source(ctx), "Number [{}] is too large", ctxText);
}
} catch (NumberFormatException ex) {
// parsing fails, go through
}
throw new ParsingException(source(ctx), "Cannot parse number [{}]", ctx.getText());
throw new ParsingException(source(ctx), "Cannot parse number [{}]", ctxText);
}
DataType type = DataType.LONG;
@ -681,4 +686,21 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
return new Literal(source(ctx), string, DataType.KEYWORD);
}
private boolean hasMinusFromParent(SqlBaseParser.NumberContext ctx) {
ParserRuleContext parentCtx = ctx.getParent();
if (parentCtx != null && parentCtx instanceof SqlBaseParser.NumericLiteralContext) {
parentCtx = parentCtx.getParent();
if (parentCtx != null && parentCtx instanceof SqlBaseParser.ConstantDefaultContext) {
parentCtx = parentCtx.getParent();
if (parentCtx != null && parentCtx instanceof SqlBaseParser.ValueExpressionDefaultContext) {
parentCtx = parentCtx.getParent();
if (parentCtx != null && parentCtx instanceof SqlBaseParser.ArithmeticUnaryContext) {
return ((ArithmeticUnaryContext) parentCtx).MINUS() != null;
}
}
}
}
return false;
}
}

View File

@ -142,7 +142,7 @@ class SqlBaseLexer extends Lexer {
public ATN getATN() { return _ATN; }
public static final String _serializedATN =
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2k\u0370\b\1\4\2\t"+
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2k\u036f\b\1\4\2\t"+
"\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"+
"\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+
@ -187,13 +187,13 @@ class SqlBaseLexer extends Lexer {
"\16a\u02ec\3a\6a\u02f0\na\ra\16a\u02f1\3a\3a\7a\u02f6\na\fa\16a\u02f9"+
"\13a\5a\u02fb\na\3a\3a\3a\3a\6a\u0301\na\ra\16a\u0302\3a\3a\5a\u0307\n"+
"a\3b\3b\5b\u030b\nb\3b\3b\3b\7b\u0310\nb\fb\16b\u0313\13b\3c\3c\3c\3c"+
"\6c\u0319\nc\rc\16c\u031a\3d\3d\3d\3d\6d\u0321\nd\rd\16d\u0322\3e\3e\3"+
"e\3e\7e\u0329\ne\fe\16e\u032c\13e\3e\3e\3f\3f\3f\3f\7f\u0334\nf\ff\16"+
"f\u0337\13f\3f\3f\3g\3g\5g\u033d\ng\3g\6g\u0340\ng\rg\16g\u0341\3h\3h"+
"\3i\3i\3j\3j\3j\3j\7j\u034c\nj\fj\16j\u034f\13j\3j\5j\u0352\nj\3j\5j\u0355"+
"\nj\3j\3j\3k\3k\3k\3k\3k\7k\u035e\nk\fk\16k\u0361\13k\3k\3k\3k\3k\3k\3"+
"l\6l\u0369\nl\rl\16l\u036a\3l\3l\3m\3m\3\u035f\2n\3\3\5\4\7\5\t\6\13\7"+
"\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17\35\20\37\21!\22#\23%\24\'\25"+
"\6c\u0319\nc\rc\16c\u031a\3d\3d\3d\6d\u0320\nd\rd\16d\u0321\3e\3e\3e\3"+
"e\7e\u0328\ne\fe\16e\u032b\13e\3e\3e\3f\3f\3f\3f\7f\u0333\nf\ff\16f\u0336"+
"\13f\3f\3f\3g\3g\5g\u033c\ng\3g\6g\u033f\ng\rg\16g\u0340\3h\3h\3i\3i\3"+
"j\3j\3j\3j\7j\u034b\nj\fj\16j\u034e\13j\3j\5j\u0351\nj\3j\5j\u0354\nj"+
"\3j\3j\3k\3k\3k\3k\3k\7k\u035d\nk\fk\16k\u0360\13k\3k\3k\3k\3k\3k\3l\6"+
"l\u0368\nl\rl\16l\u0369\3l\3l\3m\3m\3\u035e\2n\3\3\5\4\7\5\t\6\13\7\r"+
"\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17\35\20\37\21!\22#\23%\24\'\25"+
")\26+\27-\30/\31\61\32\63\33\65\34\67\359\36;\37= ?!A\"C#E$G%I&K\'M(O"+
")Q*S+U,W-Y.[/]\60_\61a\62c\63e\64g\65i\66k\67m8o9q:s;u<w=y>{?}@\177A\u0081"+
"B\u0083C\u0085D\u0087E\u0089F\u008bG\u008dH\u008fI\u0091J\u0093K\u0095"+
@ -201,7 +201,7 @@ class SqlBaseLexer extends Lexer {
"V\u00abW\u00adX\u00afY\u00b1Z\u00b3[\u00b5\\\u00b7]\u00b9^\u00bb_\u00bd"+
"`\u00bfa\u00c1b\u00c3c\u00c5d\u00c7e\u00c9f\u00cbg\u00cd\2\u00cf\2\u00d1"+
"\2\u00d3h\u00d5i\u00d7j\u00d9k\3\2\f\3\2))\4\2BBaa\5\2<<BBaa\3\2$$\3\2"+
"bb\4\2--//\3\2\62;\3\2C\\\4\2\f\f\17\17\5\2\13\f\17\17\"\"\u0392\2\3\3"+
"bb\4\2--//\3\2\62;\3\2C\\\4\2\f\f\17\17\5\2\13\f\17\17\"\"\u0390\2\3\3"+
"\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2"+
"\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3"+
"\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2"+
@ -245,10 +245,10 @@ class SqlBaseLexer extends Lexer {
"\2\2\u00ad\u02bb\3\2\2\2\u00af\u02bd\3\2\2\2\u00b1\u02bf\3\2\2\2\u00b3"+
"\u02c1\3\2\2\2\u00b5\u02c3\3\2\2\2\u00b7\u02c5\3\2\2\2\u00b9\u02c8\3\2"+
"\2\2\u00bb\u02ca\3\2\2\2\u00bd\u02cc\3\2\2\2\u00bf\u02d8\3\2\2\2\u00c1"+
"\u0306\3\2\2\2\u00c3\u030a\3\2\2\2\u00c5\u0314\3\2\2\2\u00c7\u0320\3\2"+
"\2\2\u00c9\u0324\3\2\2\2\u00cb\u032f\3\2\2\2\u00cd\u033a\3\2\2\2\u00cf"+
"\u0343\3\2\2\2\u00d1\u0345\3\2\2\2\u00d3\u0347\3\2\2\2\u00d5\u0358\3\2"+
"\2\2\u00d7\u0368\3\2\2\2\u00d9\u036e\3\2\2\2\u00db\u00dc\7*\2\2\u00dc"+
"\u0306\3\2\2\2\u00c3\u030a\3\2\2\2\u00c5\u0314\3\2\2\2\u00c7\u031f\3\2"+
"\2\2\u00c9\u0323\3\2\2\2\u00cb\u032e\3\2\2\2\u00cd\u0339\3\2\2\2\u00cf"+
"\u0342\3\2\2\2\u00d1\u0344\3\2\2\2\u00d3\u0346\3\2\2\2\u00d5\u0357\3\2"+
"\2\2\u00d7\u0367\3\2\2\2\u00d9\u036d\3\2\2\2\u00db\u00dc\7*\2\2\u00dc"+
"\4\3\2\2\2\u00dd\u00de\7+\2\2\u00de\6\3\2\2\2\u00df\u00e0\7.\2\2\u00e0"+
"\b\3\2\2\2\u00e1\u00e2\7<\2\2\u00e2\n\3\2\2\2\u00e3\u00e4\7C\2\2\u00e4"+
"\u00e5\7N\2\2\u00e5\u00e6\7N\2\2\u00e6\f\3\2\2\2\u00e7\u00e8\7C\2\2\u00e8"+
@ -408,38 +408,37 @@ class SqlBaseLexer extends Lexer {
"\2\2\u0313\u0311\3\2\2\2\u0314\u0318\5\u00cfh\2\u0315\u0319\5\u00d1i\2"+
"\u0316\u0319\5\u00cfh\2\u0317\u0319\t\4\2\2\u0318\u0315\3\2\2\2\u0318"+
"\u0316\3\2\2\2\u0318\u0317\3\2\2\2\u0319\u031a\3\2\2\2\u031a\u0318\3\2"+
"\2\2\u031a\u031b\3\2\2\2\u031b\u00c6\3\2\2\2\u031c\u0321\5\u00d1i\2\u031d"+
"\u0321\5\u00cfh\2\u031e\u0321\t\3\2\2\u031f\u0321\5\u00b1Y\2\u0320\u031c"+
"\3\2\2\2\u0320\u031d\3\2\2\2\u0320\u031e\3\2\2\2\u0320\u031f\3\2\2\2\u0321"+
"\u0322\3\2\2\2\u0322\u0320\3\2\2\2\u0322\u0323\3\2\2\2\u0323\u00c8\3\2"+
"\2\2\u0324\u032a\7$\2\2\u0325\u0329\n\5\2\2\u0326\u0327\7$\2\2\u0327\u0329"+
"\7$\2\2\u0328\u0325\3\2\2\2\u0328\u0326\3\2\2\2\u0329\u032c\3\2\2\2\u032a"+
"\u0328\3\2\2\2\u032a\u032b\3\2\2\2\u032b\u032d\3\2\2\2\u032c\u032a\3\2"+
"\2\2\u032d\u032e\7$\2\2\u032e\u00ca\3\2\2\2\u032f\u0335\7b\2\2\u0330\u0334"+
"\n\6\2\2\u0331\u0332\7b\2\2\u0332\u0334\7b\2\2\u0333\u0330\3\2\2\2\u0333"+
"\u0331\3\2\2\2\u0334\u0337\3\2\2\2\u0335\u0333\3\2\2\2\u0335\u0336\3\2"+
"\2\2\u0336\u0338\3\2\2\2\u0337\u0335\3\2\2\2\u0338\u0339\7b\2\2\u0339"+
"\u00cc\3\2\2\2\u033a\u033c\7G\2\2\u033b\u033d\t\7\2\2\u033c\u033b\3\2"+
"\2\2\u033c\u033d\3\2\2\2\u033d\u033f\3\2\2\2\u033e\u0340\5\u00cfh\2\u033f"+
"\u033e\3\2\2\2\u0340\u0341\3\2\2\2\u0341\u033f\3\2\2\2\u0341\u0342\3\2"+
"\2\2\u0342\u00ce\3\2\2\2\u0343\u0344\t\b\2\2\u0344\u00d0\3\2\2\2\u0345"+
"\u0346\t\t\2\2\u0346\u00d2\3\2\2\2\u0347\u0348\7/\2\2\u0348\u0349\7/\2"+
"\2\u0349\u034d\3\2\2\2\u034a\u034c\n\n\2\2\u034b\u034a\3\2\2\2\u034c\u034f"+
"\3\2\2\2\u034d\u034b\3\2\2\2\u034d\u034e\3\2\2\2\u034e\u0351\3\2\2\2\u034f"+
"\u034d\3\2\2\2\u0350\u0352\7\17\2\2\u0351\u0350\3\2\2\2\u0351\u0352\3"+
"\2\2\2\u0352\u0354\3\2\2\2\u0353\u0355\7\f\2\2\u0354\u0353\3\2\2\2\u0354"+
"\u0355\3\2\2\2\u0355\u0356\3\2\2\2\u0356\u0357\bj\2\2\u0357\u00d4\3\2"+
"\2\2\u0358\u0359\7\61\2\2\u0359\u035a\7,\2\2\u035a\u035f\3\2\2\2\u035b"+
"\u035e\5\u00d5k\2\u035c\u035e\13\2\2\2\u035d\u035b\3\2\2\2\u035d\u035c"+
"\3\2\2\2\u035e\u0361\3\2\2\2\u035f\u0360\3\2\2\2\u035f\u035d\3\2\2\2\u0360"+
"\u0362\3\2\2\2\u0361\u035f\3\2\2\2\u0362\u0363\7,\2\2\u0363\u0364\7\61"+
"\2\2\u0364\u0365\3\2\2\2\u0365\u0366\bk\2\2\u0366\u00d6\3\2\2\2\u0367"+
"\u0369\t\13\2\2\u0368\u0367\3\2\2\2\u0369\u036a\3\2\2\2\u036a\u0368\3"+
"\2\2\2\u036a\u036b\3\2\2\2\u036b\u036c\3\2\2\2\u036c\u036d\bl\2\2\u036d"+
"\u00d8\3\2\2\2\u036e\u036f\13\2\2\2\u036f\u00da\3\2\2\2\"\2\u02af\u02d0"+
"\u02d2\u02da\u02df\u02e5\u02ec\u02f1\u02f7\u02fa\u0302\u0306\u030a\u030f"+
"\u0311\u0318\u031a\u0320\u0322\u0328\u032a\u0333\u0335\u033c\u0341\u034d"+
"\u0351\u0354\u035d\u035f\u036a\3\2\3\2";
"\2\2\u031a\u031b\3\2\2\2\u031b\u00c6\3\2\2\2\u031c\u0320\5\u00d1i\2\u031d"+
"\u0320\5\u00cfh\2\u031e\u0320\7a\2\2\u031f\u031c\3\2\2\2\u031f\u031d\3"+
"\2\2\2\u031f\u031e\3\2\2\2\u0320\u0321\3\2\2\2\u0321\u031f\3\2\2\2\u0321"+
"\u0322\3\2\2\2\u0322\u00c8\3\2\2\2\u0323\u0329\7$\2\2\u0324\u0328\n\5"+
"\2\2\u0325\u0326\7$\2\2\u0326\u0328\7$\2\2\u0327\u0324\3\2\2\2\u0327\u0325"+
"\3\2\2\2\u0328\u032b\3\2\2\2\u0329\u0327\3\2\2\2\u0329\u032a\3\2\2\2\u032a"+
"\u032c\3\2\2\2\u032b\u0329\3\2\2\2\u032c\u032d\7$\2\2\u032d\u00ca\3\2"+
"\2\2\u032e\u0334\7b\2\2\u032f\u0333\n\6\2\2\u0330\u0331\7b\2\2\u0331\u0333"+
"\7b\2\2\u0332\u032f\3\2\2\2\u0332\u0330\3\2\2\2\u0333\u0336\3\2\2\2\u0334"+
"\u0332\3\2\2\2\u0334\u0335\3\2\2\2\u0335\u0337\3\2\2\2\u0336\u0334\3\2"+
"\2\2\u0337\u0338\7b\2\2\u0338\u00cc\3\2\2\2\u0339\u033b\7G\2\2\u033a\u033c"+
"\t\7\2\2\u033b\u033a\3\2\2\2\u033b\u033c\3\2\2\2\u033c\u033e\3\2\2\2\u033d"+
"\u033f\5\u00cfh\2\u033e\u033d\3\2\2\2\u033f\u0340\3\2\2\2\u0340\u033e"+
"\3\2\2\2\u0340\u0341\3\2\2\2\u0341\u00ce\3\2\2\2\u0342\u0343\t\b\2\2\u0343"+
"\u00d0\3\2\2\2\u0344\u0345\t\t\2\2\u0345\u00d2\3\2\2\2\u0346\u0347\7/"+
"\2\2\u0347\u0348\7/\2\2\u0348\u034c\3\2\2\2\u0349\u034b\n\n\2\2\u034a"+
"\u0349\3\2\2\2\u034b\u034e\3\2\2\2\u034c\u034a\3\2\2\2\u034c\u034d\3\2"+
"\2\2\u034d\u0350\3\2\2\2\u034e\u034c\3\2\2\2\u034f\u0351\7\17\2\2\u0350"+
"\u034f\3\2\2\2\u0350\u0351\3\2\2\2\u0351\u0353\3\2\2\2\u0352\u0354\7\f"+
"\2\2\u0353\u0352\3\2\2\2\u0353\u0354\3\2\2\2\u0354\u0355\3\2\2\2\u0355"+
"\u0356\bj\2\2\u0356\u00d4\3\2\2\2\u0357\u0358\7\61\2\2\u0358\u0359\7,"+
"\2\2\u0359\u035e\3\2\2\2\u035a\u035d\5\u00d5k\2\u035b\u035d\13\2\2\2\u035c"+
"\u035a\3\2\2\2\u035c\u035b\3\2\2\2\u035d\u0360\3\2\2\2\u035e\u035f\3\2"+
"\2\2\u035e\u035c\3\2\2\2\u035f\u0361\3\2\2\2\u0360\u035e\3\2\2\2\u0361"+
"\u0362\7,\2\2\u0362\u0363\7\61\2\2\u0363\u0364\3\2\2\2\u0364\u0365\bk"+
"\2\2\u0365\u00d6\3\2\2\2\u0366\u0368\t\13\2\2\u0367\u0366\3\2\2\2\u0368"+
"\u0369\3\2\2\2\u0369\u0367\3\2\2\2\u0369\u036a\3\2\2\2\u036a\u036b\3\2"+
"\2\2\u036b\u036c\bl\2\2\u036c\u00d8\3\2\2\2\u036d\u036e\13\2\2\2\u036e"+
"\u00da\3\2\2\2\"\2\u02af\u02d0\u02d2\u02da\u02df\u02e5\u02ec\u02f1\u02f7"+
"\u02fa\u0302\u0306\u030a\u030f\u0311\u0318\u031a\u031f\u0321\u0327\u0329"+
"\u0332\u0334\u033b\u0340\u034c\u0350\u0353\u035c\u035e\u0369\3\2\3\2";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {

View File

@ -3748,9 +3748,54 @@ class SqlBaseParser extends Parser {
enterOuterAlt(_localctx, 1);
{
setState(539);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,72,_ctx) ) {
case 1:
switch (_input.LA(1)) {
case T__0:
case ANALYZE:
case ANALYZED:
case CAST:
case CATALOGS:
case COLUMNS:
case DEBUG:
case EXECUTABLE:
case EXPLAIN:
case EXTRACT:
case FALSE:
case FORMAT:
case FUNCTIONS:
case GRAPHVIZ:
case LEFT:
case MAPPED:
case NULL:
case OPTIMIZED:
case PARSED:
case PHYSICAL:
case PLAN:
case RIGHT:
case RLIKE:
case QUERY:
case SCHEMAS:
case SHOW:
case SYS:
case TABLES:
case TEXT:
case TRUE:
case TYPE:
case TYPES:
case VERIFY:
case FUNCTION_ESC:
case DATE_ESC:
case TIME_ESC:
case TIMESTAMP_ESC:
case GUID_ESC:
case ASTERISK:
case PARAM:
case STRING:
case INTEGER_VALUE:
case DECIMAL_VALUE:
case IDENTIFIER:
case DIGIT_IDENTIFIER:
case QUOTED_IDENTIFIER:
case BACKQUOTED_IDENTIFIER:
{
_localctx = new ValueExpressionDefaultContext(_localctx);
_ctx = _localctx;
@ -3760,7 +3805,8 @@ class SqlBaseParser extends Parser {
primaryExpression();
}
break;
case 2:
case PLUS:
case MINUS:
{
_localctx = new ArithmeticUnaryContext(_localctx);
_ctx = _localctx;
@ -3777,6 +3823,8 @@ class SqlBaseParser extends Parser {
valueExpression(4);
}
break;
default:
throw new NoViableAltException(this);
}
_ctx.stop = _input.LT(-1);
setState(553);
@ -4861,8 +4909,6 @@ class SqlBaseParser extends Parser {
match(NULL);
}
break;
case PLUS:
case MINUS:
case INTEGER_VALUE:
case DECIMAL_VALUE:
_localctx = new NumericLiteralContext(_localctx);
@ -5592,8 +5638,6 @@ class SqlBaseParser extends Parser {
}
public static class DecimalLiteralContext extends NumberContext {
public TerminalNode DECIMAL_VALUE() { return getToken(SqlBaseParser.DECIMAL_VALUE, 0); }
public TerminalNode PLUS() { return getToken(SqlBaseParser.PLUS, 0); }
public TerminalNode MINUS() { return getToken(SqlBaseParser.MINUS, 0); }
public DecimalLiteralContext(NumberContext ctx) { copyFrom(ctx); }
@Override
public void enterRule(ParseTreeListener listener) {
@ -5611,8 +5655,6 @@ class SqlBaseParser extends Parser {
}
public static class IntegerLiteralContext extends NumberContext {
public TerminalNode INTEGER_VALUE() { return getToken(SqlBaseParser.INTEGER_VALUE, 0); }
public TerminalNode PLUS() { return getToken(SqlBaseParser.PLUS, 0); }
public TerminalNode MINUS() { return getToken(SqlBaseParser.MINUS, 0); }
public IntegerLiteralContext(NumberContext ctx) { copyFrom(ctx); }
@Override
public void enterRule(ParseTreeListener listener) {
@ -5632,55 +5674,27 @@ class SqlBaseParser extends Parser {
public final NumberContext number() throws RecognitionException {
NumberContext _localctx = new NumberContext(_ctx, getState());
enterRule(_localctx, 94, RULE_number);
int _la;
try {
setState(712);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,95,_ctx) ) {
case 1:
setState(706);
switch (_input.LA(1)) {
case DECIMAL_VALUE:
_localctx = new DecimalLiteralContext(_localctx);
enterOuterAlt(_localctx, 1);
{
setState(705);
_la = _input.LA(1);
if (_la==PLUS || _la==MINUS) {
{
setState(704);
_la = _input.LA(1);
if ( !(_la==PLUS || _la==MINUS) ) {
_errHandler.recoverInline(this);
} else {
consume();
}
}
}
setState(707);
setState(704);
match(DECIMAL_VALUE);
}
break;
case 2:
case INTEGER_VALUE:
_localctx = new IntegerLiteralContext(_localctx);
enterOuterAlt(_localctx, 2);
{
setState(709);
_la = _input.LA(1);
if (_la==PLUS || _la==MINUS) {
{
setState(708);
_la = _input.LA(1);
if ( !(_la==PLUS || _la==MINUS) ) {
_errHandler.recoverInline(this);
} else {
consume();
}
}
}
setState(711);
setState(705);
match(INTEGER_VALUE);
}
break;
default:
throw new NoViableAltException(this);
}
}
catch (RecognitionException re) {
@ -5723,7 +5737,7 @@ class SqlBaseParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(714);
setState(708);
_la = _input.LA(1);
if ( !(_la==PARAM || _la==STRING) ) {
_errHandler.recoverInline(this);
@ -5795,7 +5809,7 @@ class SqlBaseParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(716);
setState(710);
_la = _input.LA(1);
if ( !(((((_la - 6)) & ~0x3f) == 0 && ((1L << (_la - 6)) & ((1L << (ANALYZE - 6)) | (1L << (ANALYZED - 6)) | (1L << (CATALOGS - 6)) | (1L << (COLUMNS - 6)) | (1L << (DEBUG - 6)) | (1L << (EXECUTABLE - 6)) | (1L << (EXPLAIN - 6)) | (1L << (FORMAT - 6)) | (1L << (FUNCTIONS - 6)) | (1L << (GRAPHVIZ - 6)) | (1L << (MAPPED - 6)) | (1L << (OPTIMIZED - 6)) | (1L << (PARSED - 6)) | (1L << (PHYSICAL - 6)) | (1L << (PLAN - 6)) | (1L << (RLIKE - 6)) | (1L << (QUERY - 6)) | (1L << (SCHEMAS - 6)) | (1L << (SHOW - 6)) | (1L << (SYS - 6)) | (1L << (TABLES - 6)) | (1L << (TEXT - 6)) | (1L << (TYPE - 6)) | (1L << (TYPES - 6)) | (1L << (VERIFY - 6)))) != 0)) ) {
_errHandler.recoverInline(this);
@ -5846,7 +5860,7 @@ class SqlBaseParser extends Parser {
}
public static final String _serializedATN =
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3l\u02d1\4\2\t\2\4"+
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3l\u02cb\4\2\t\2\4"+
"\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t"+
"\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+
@ -5898,237 +5912,234 @@ class SqlBaseParser extends Parser {
"\3(\3(\3(\3(\3(\3(\3(\3(\3(\3(\3(\3(\3(\3(\3(\3(\3(\5(\u0296\n(\3)\3)"+
"\3*\3*\3+\3+\3,\3,\3,\7,\u02a1\n,\f,\16,\u02a4\13,\3,\3,\3-\3-\5-\u02aa"+
"\n-\3.\3.\3.\5.\u02af\n.\3.\3.\3.\3.\5.\u02b5\n.\3.\5.\u02b8\n.\3/\3/"+
"\5/\u02bc\n/\3\60\3\60\3\60\5\60\u02c1\n\60\3\61\5\61\u02c4\n\61\3\61"+
"\3\61\5\61\u02c8\n\61\3\61\5\61\u02cb\n\61\3\62\3\62\3\63\3\63\3\63\2"+
"\4.<\64\2\4\6\b\n\f\16\20\22\24\26\30\32\34\36 \"$&(*,.\60\62\64\668:"+
"<>@BDFHJLNPRTVXZ\\^`bd\2\20\b\2\7\7\t\t\31\31,,\62\62\66\66\4\2\"\"BB"+
"\4\2\t\t\62\62\4\2\37\37%%\3\2\25\26\4\2\7\7aa\4\2\r\r\25\25\4\2\7\7\27"+
"\27\3\2XY\3\2Z\\\3\2RW\4\2\35\35CC\3\2_`\20\2\b\t\22\24\31\31\33\33\36"+
"\36!\",,\62\62\668:<>?ABDEGG\u0328\2f\3\2\2\2\4i\3\2\2\2\6\u00d0\3\2\2"+
"\2\b\u00db\3\2\2\2\n\u00df\3\2\2\2\f\u00f4\3\2\2\2\16\u00fb\3\2\2\2\20"+
"\u00fd\3\2\2\2\22\u0101\3\2\2\2\24\u011d\3\2\2\2\26\u0127\3\2\2\2\30\u0131"+
"\3\2\2\2\32\u0140\3\2\2\2\34\u0142\3\2\2\2\36\u0148\3\2\2\2 \u014a\3\2"+
"\2\2\"\u0151\3\2\2\2$\u0163\3\2\2\2&\u0174\3\2\2\2(\u0184\3\2\2\2*\u019f"+
"\3\2\2\2,\u01a1\3\2\2\2.\u01c2\3\2\2\2\60\u01d3\3\2\2\2\62\u01d6\3\2\2"+
"\2\64\u0208\3\2\2\2\66\u020a\3\2\2\28\u020d\3\2\2\2:\u0217\3\2\2\2<\u021d"+
"\3\2\2\2>\u0241\3\2\2\2@\u0248\3\2\2\2B\u024a\3\2\2\2D\u0256\3\2\2\2F"+
"\u0258\3\2\2\2H\u0264\3\2\2\2J\u0266\3\2\2\2L\u027a\3\2\2\2N\u0295\3\2"+
"\2\2P\u0297\3\2\2\2R\u0299\3\2\2\2T\u029b\3\2\2\2V\u02a2\3\2\2\2X\u02a9"+
"\3\2\2\2Z\u02b7\3\2\2\2\\\u02bb\3\2\2\2^\u02c0\3\2\2\2`\u02ca\3\2\2\2"+
"b\u02cc\3\2\2\2d\u02ce\3\2\2\2fg\5\6\4\2gh\7\2\2\3h\3\3\2\2\2ij\5,\27"+
"\2jk\7\2\2\3k\5\3\2\2\2l\u00d1\5\b\5\2m{\7\33\2\2nw\7\3\2\2op\78\2\2p"+
"v\t\2\2\2qr\7\36\2\2rv\t\3\2\2st\7G\2\2tv\5R*\2uo\3\2\2\2uq\3\2\2\2us"+
"\3\2\2\2vy\3\2\2\2wu\3\2\2\2wx\3\2\2\2xz\3\2\2\2yw\3\2\2\2z|\7\4\2\2{"+
"n\3\2\2\2{|\3\2\2\2|}\3\2\2\2}\u00d1\5\6\4\2~\u008a\7\24\2\2\177\u0086"+
"\7\3\2\2\u0080\u0081\78\2\2\u0081\u0085\t\4\2\2\u0082\u0083\7\36\2\2\u0083"+
"\u0085\t\3\2\2\u0084\u0080\3\2\2\2\u0084\u0082\3\2\2\2\u0085\u0088\3\2"+
"\2\2\u0086\u0084\3\2\2\2\u0086\u0087\3\2\2\2\u0087\u0089\3\2\2\2\u0088"+
"\u0086\3\2\2\2\u0089\u008b\7\4\2\2\u008a\177\3\2\2\2\u008a\u008b\3\2\2"+
"\2\u008b\u008c\3\2\2\2\u008c\u00d1\5\6\4\2\u008d\u008e\7>\2\2\u008e\u0091"+
"\7A\2\2\u008f\u0092\5\66\34\2\u0090\u0092\5Z.\2\u0091\u008f\3\2\2\2\u0091"+
"\u0090\3\2\2\2\u0091\u0092\3\2\2\2\u0092\u00d1\3\2\2\2\u0093\u0094\7>"+
"\2\2\u0094\u0095\7\23\2\2\u0095\u0098\t\5\2\2\u0096\u0099\5\66\34\2\u0097"+
"\u0099\5Z.\2\u0098\u0096\3\2\2\2\u0098\u0097\3\2\2\2\u0099\u00d1\3\2\2"+
"\2\u009a\u009d\t\6\2\2\u009b\u009e\5\66\34\2\u009c\u009e\5Z.\2\u009d\u009b"+
"\3\2\2\2\u009d\u009c\3\2\2\2\u009e\u00d1\3\2\2\2\u009f\u00a0\7>\2\2\u00a0"+
"\u00a2\7!\2\2\u00a1\u00a3\5\66\34\2\u00a2\u00a1\3\2\2\2\u00a2\u00a3\3"+
"\2\2\2\u00a3\u00d1\3\2\2\2\u00a4\u00a5\7>\2\2\u00a5\u00d1\7<\2\2\u00a6"+
"\u00a7\7?\2\2\u00a7\u00d1\7\22\2\2\u00a8\u00a9\7?\2\2\u00a9\u00ac\7A\2"+
"\2\u00aa\u00ab\7\21\2\2\u00ab\u00ad\5\66\34\2\u00ac\u00aa\3\2\2\2\u00ac"+
"\u00ad\3\2\2\2\u00ad\u00b0\3\2\2\2\u00ae\u00b1\5\66\34\2\u00af\u00b1\5"+
"Z.\2\u00b0\u00ae\3\2\2\2\u00b0\u00af\3\2\2\2\u00b0\u00b1\3\2\2\2\u00b1"+
"\u00bb\3\2\2\2\u00b2\u00b3\7D\2\2\u00b3\u00b8\5b\62\2\u00b4\u00b5\7\5"+
"\2\2\u00b5\u00b7\5b\62\2\u00b6\u00b4\3\2\2\2\u00b7\u00ba\3\2\2\2\u00b8"+
"\u00b6\3\2\2\2\u00b8\u00b9\3\2\2\2\u00b9\u00bc\3\2\2\2\u00ba\u00b8\3\2"+
"\2\2\u00bb\u00b2\3\2\2\2\u00bb\u00bc\3\2\2\2\u00bc\u00d1\3\2\2\2\u00bd"+
"\u00be\7?\2\2\u00be\u00c1\7\23\2\2\u00bf\u00c0\7\21\2\2\u00c0\u00c2\5"+
"b\62\2\u00c1\u00bf\3\2\2\2\u00c1\u00c2\3\2\2\2\u00c2\u00c6\3\2\2\2\u00c3"+
"\u00c4\7@\2\2\u00c4\u00c7\5\66\34\2\u00c5\u00c7\5Z.\2\u00c6\u00c3\3\2"+
"\2\2\u00c6\u00c5\3\2\2\2\u00c6\u00c7\3\2\2\2\u00c7\u00c9\3\2\2\2\u00c8"+
"\u00ca\5\66\34\2\u00c9\u00c8\3\2\2\2\u00c9\u00ca\3\2\2\2\u00ca\u00d1\3"+
"\2\2\2\u00cb\u00cc\7?\2\2\u00cc\u00d1\7E\2\2\u00cd\u00ce\7?\2\2\u00ce"+
"\u00cf\7@\2\2\u00cf\u00d1\7E\2\2\u00d0l\3\2\2\2\u00d0m\3\2\2\2\u00d0~"+
"\3\2\2\2\u00d0\u008d\3\2\2\2\u00d0\u0093\3\2\2\2\u00d0\u009a\3\2\2\2\u00d0"+
"\u009f\3\2\2\2\u00d0\u00a4\3\2\2\2\u00d0\u00a6\3\2\2\2\u00d0\u00a8\3\2"+
"\2\2\u00d0\u00bd\3\2\2\2\u00d0\u00cb\3\2\2\2\u00d0\u00cd\3\2\2\2\u00d1"+
"\7\3\2\2\2\u00d2\u00d3\7I\2\2\u00d3\u00d8\5\34\17\2\u00d4\u00d5\7\5\2"+
"\2\u00d5\u00d7\5\34\17\2\u00d6\u00d4\3\2\2\2\u00d7\u00da\3\2\2\2\u00d8"+
"\u00d6\3\2\2\2\u00d8\u00d9\3\2\2\2\u00d9\u00dc\3\2\2\2\u00da\u00d8\3\2"+
"\2\2\u00db\u00d2\3\2\2\2\u00db\u00dc\3\2\2\2\u00dc\u00dd\3\2\2\2\u00dd"+
"\u00de\5\n\6\2\u00de\t\3\2\2\2\u00df\u00ea\5\16\b\2\u00e0\u00e1\7\64\2"+
"\2\u00e1\u00e2\7\17\2\2\u00e2\u00e7\5\20\t\2\u00e3\u00e4\7\5\2\2\u00e4"+
"\u00e6\5\20\t\2\u00e5\u00e3\3\2\2\2\u00e6\u00e9\3\2\2\2\u00e7\u00e5\3"+
"\2\2\2\u00e7\u00e8\3\2\2\2\u00e8\u00eb\3\2\2\2\u00e9\u00e7\3\2\2\2\u00ea"+
"\u00e0\3\2\2\2\u00ea\u00eb\3\2\2\2\u00eb\u00ed\3\2\2\2\u00ec\u00ee\5\f"+
"\7\2\u00ed\u00ec\3\2\2\2\u00ed\u00ee\3\2\2\2\u00ee\13\3\2\2\2\u00ef\u00f0"+
"\7+\2\2\u00f0\u00f5\t\7\2\2\u00f1\u00f2\7L\2\2\u00f2\u00f3\t\7\2\2\u00f3"+
"\u00f5\7Q\2\2\u00f4\u00ef\3\2\2\2\u00f4\u00f1\3\2\2\2\u00f5\r\3\2\2\2"+
"\u00f6\u00fc\5\22\n\2\u00f7\u00f8\7\3\2\2\u00f8\u00f9\5\n\6\2\u00f9\u00fa"+
"\7\4\2\2\u00fa\u00fc\3\2\2\2\u00fb\u00f6\3\2\2\2\u00fb\u00f7\3\2\2\2\u00fc"+
"\17\3\2\2\2\u00fd\u00ff\5,\27\2\u00fe\u0100\t\b\2\2\u00ff\u00fe\3\2\2"+
"\2\u00ff\u0100\3\2\2\2\u0100\21\3\2\2\2\u0101\u0103\7=\2\2\u0102\u0104"+
"\5\36\20\2\u0103\u0102\3\2\2\2\u0103\u0104\3\2\2\2\u0104\u0105\3\2\2\2"+
"\u0105\u010a\5 \21\2\u0106\u0107\7\5\2\2\u0107\u0109\5 \21\2\u0108\u0106"+
"\3\2\2\2\u0109\u010c\3\2\2\2\u010a\u0108\3\2\2\2\u010a\u010b\3\2\2\2\u010b"+
"\u010e\3\2\2\2\u010c\u010a\3\2\2\2\u010d\u010f\5\24\13\2\u010e\u010d\3"+
"\2\2\2\u010e\u010f\3\2\2\2\u010f\u0112\3\2\2\2\u0110\u0111\7H\2\2\u0111"+
"\u0113\5.\30\2\u0112\u0110\3\2\2\2\u0112\u0113\3\2\2\2\u0113\u0117\3\2"+
"\2\2\u0114\u0115\7#\2\2\u0115\u0116\7\17\2\2\u0116\u0118\5\26\f\2\u0117"+
"\u0114\3\2\2\2\u0117\u0118\3\2\2\2\u0118\u011b\3\2\2\2\u0119\u011a\7$"+
"\2\2\u011a\u011c\5.\30\2\u011b\u0119\3\2\2\2\u011b\u011c\3\2\2\2\u011c"+
"\23\3\2\2\2\u011d\u011e\7\37\2\2\u011e\u0123\5\"\22\2\u011f\u0120\7\5"+
"\2\2\u0120\u0122\5\"\22\2\u0121\u011f\3\2\2\2\u0122\u0125\3\2\2\2\u0123"+
"\u0121\3\2\2\2\u0123\u0124\3\2\2\2\u0124\25\3\2\2\2\u0125\u0123\3\2\2"+
"\2\u0126\u0128\5\36\20\2\u0127\u0126\3\2\2\2\u0127\u0128\3\2\2\2\u0128"+
"\u0129\3\2\2\2\u0129\u012e\5\30\r\2\u012a\u012b\7\5\2\2\u012b\u012d\5"+
"\30\r\2\u012c\u012a\3\2\2\2\u012d\u0130\3\2\2\2\u012e\u012c\3\2\2\2\u012e"+
"\u012f\3\2\2\2\u012f\27\3\2\2\2\u0130\u012e\3\2\2\2\u0131\u0132\5\32\16"+
"\2\u0132\31\3\2\2\2\u0133\u013c\7\3\2\2\u0134\u0139\5,\27\2\u0135\u0136"+
"\7\5\2\2\u0136\u0138\5,\27\2\u0137\u0135\3\2\2\2\u0138\u013b\3\2\2\2\u0139"+
"\u0137\3\2\2\2\u0139\u013a\3\2\2\2\u013a\u013d\3\2\2\2\u013b\u0139\3\2"+
"\2\2\u013c\u0134\3\2\2\2\u013c\u013d\3\2\2\2\u013d\u013e\3\2\2\2\u013e"+
"\u0141\7\4\2\2\u013f\u0141\5,\27\2\u0140\u0133\3\2\2\2\u0140\u013f\3\2"+
"\2\2\u0141\33\3\2\2\2\u0142\u0143\5X-\2\u0143\u0144\7\f\2\2\u0144\u0145"+
"\7\3\2\2\u0145\u0146\5\n\6\2\u0146\u0147\7\4\2\2\u0147\35\3\2\2\2\u0148"+
"\u0149\t\t\2\2\u0149\37\3\2\2\2\u014a\u014f\5,\27\2\u014b\u014d\7\f\2"+
"\2\u014c\u014b\3\2\2\2\u014c\u014d\3\2\2\2\u014d\u014e\3\2\2\2\u014e\u0150"+
"\5X-\2\u014f\u014c\3\2\2\2\u014f\u0150\3\2\2\2\u0150!\3\2\2\2\u0151\u0155"+
"\5*\26\2\u0152\u0154\5$\23\2\u0153\u0152\3\2\2\2\u0154\u0157\3\2\2\2\u0155"+
"\u0153\3\2\2\2\u0155\u0156\3\2\2\2\u0156#\3\2\2\2\u0157\u0155\3\2\2\2"+
"\u0158\u0159\5&\24\2\u0159\u015a\7(\2\2\u015a\u015c\5*\26\2\u015b\u015d"+
"\5(\25\2\u015c\u015b\3\2\2\2\u015c\u015d\3\2\2\2\u015d\u0164\3\2\2\2\u015e"+
"\u015f\7.\2\2\u015f\u0160\5&\24\2\u0160\u0161\7(\2\2\u0161\u0162\5*\26"+
"\2\u0162\u0164\3\2\2\2\u0163\u0158\3\2\2\2\u0163\u015e\3\2\2\2\u0164%"+
"\3\2\2\2\u0165\u0167\7&\2\2\u0166\u0165\3\2\2\2\u0166\u0167\3\2\2\2\u0167"+
"\u0175\3\2\2\2\u0168\u016a\7)\2\2\u0169\u016b\7\65\2\2\u016a\u0169\3\2"+
"\2\2\u016a\u016b\3\2\2\2\u016b\u0175\3\2\2\2\u016c\u016e\79\2\2\u016d"+
"\u016f\7\65\2\2\u016e\u016d\3\2\2\2\u016e\u016f\3\2\2\2\u016f\u0175\3"+
"\2\2\2\u0170\u0172\7 \2\2\u0171\u0173\7\65\2\2\u0172\u0171\3\2\2\2\u0172"+
"\u0173\3\2\2\2\u0173\u0175\3\2\2\2\u0174\u0166\3\2\2\2\u0174\u0168\3\2"+
"\2\2\u0174\u016c\3\2\2\2\u0174\u0170\3\2\2\2\u0175\'\3\2\2\2\u0176\u0177"+
"\7\61\2\2\u0177\u0185\5.\30\2\u0178\u0179\7F\2\2\u0179\u017a\7\3\2\2\u017a"+
"\u017f\5X-\2\u017b\u017c\7\5\2\2\u017c\u017e\5X-\2\u017d\u017b\3\2\2\2"+
"\u017e\u0181\3\2\2\2\u017f\u017d\3\2\2\2\u017f\u0180\3\2\2\2\u0180\u0182"+
"\3\2\2\2\u0181\u017f\3\2\2\2\u0182\u0183\7\4\2\2\u0183\u0185\3\2\2\2\u0184"+
"\u0176\3\2\2\2\u0184\u0178\3\2\2\2\u0185)\3\2\2\2\u0186\u018b\5Z.\2\u0187"+
"\u0189\7\f\2\2\u0188\u0187\3\2\2\2\u0188\u0189\3\2\2\2\u0189\u018a\3\2"+
"\2\2\u018a\u018c\5V,\2\u018b\u0188\3\2\2\2\u018b\u018c\3\2\2\2\u018c\u01a0"+
"\3\2\2\2\u018d\u018e\7\3\2\2\u018e\u018f\5\n\6\2\u018f\u0194\7\4\2\2\u0190"+
"\u0192\7\f\2\2\u0191\u0190\3\2\2\2\u0191\u0192\3\2\2\2\u0192\u0193\3\2"+
"\2\2\u0193\u0195\5V,\2\u0194\u0191\3\2\2\2\u0194\u0195\3\2\2\2\u0195\u01a0"+
"\3\2\2\2\u0196\u0197\7\3\2\2\u0197\u0198\5\"\22\2\u0198\u019d\7\4\2\2"+
"\u0199\u019b\7\f\2\2\u019a\u0199\3\2\2\2\u019a\u019b\3\2\2\2\u019b\u019c"+
"\3\2\2\2\u019c\u019e\5V,\2\u019d\u019a\3\2\2\2\u019d\u019e\3\2\2\2\u019e"+
"\u01a0\3\2\2\2\u019f\u0186\3\2\2\2\u019f\u018d\3\2\2\2\u019f\u0196\3\2"+
"\2\2\u01a0+\3\2\2\2\u01a1\u01a2\5.\30\2\u01a2-\3\2\2\2\u01a3\u01a4\b\30"+
"\1\2\u01a4\u01a5\7/\2\2\u01a5\u01c3\5.\30\n\u01a6\u01a7\7\32\2\2\u01a7"+
"\u01a8\7\3\2\2\u01a8\u01a9\5\b\5\2\u01a9\u01aa\7\4\2\2\u01aa\u01c3\3\2"+
"\2\2\u01ab\u01ac\7;\2\2\u01ac\u01ad\7\3\2\2\u01ad\u01ae\5b\62\2\u01ae"+
"\u01af\5\60\31\2\u01af\u01b0\7\4\2\2\u01b0\u01c3\3\2\2\2\u01b1\u01b2\7"+
"-\2\2\u01b2\u01b3\7\3\2\2\u01b3\u01b4\5V,\2\u01b4\u01b5\7\5\2\2\u01b5"+
"\u01b6\5b\62\2\u01b6\u01b7\5\60\31\2\u01b7\u01b8\7\4\2\2\u01b8\u01c3\3"+
"\2\2\2\u01b9\u01ba\7-\2\2\u01ba\u01bb\7\3\2\2\u01bb\u01bc\5b\62\2\u01bc"+
"\u01bd\7\5\2\2\u01bd\u01be\5b\62\2\u01be\u01bf\5\60\31\2\u01bf\u01c0\7"+
"\4\2\2\u01c0\u01c3\3\2\2\2\u01c1\u01c3\5\62\32\2\u01c2\u01a3\3\2\2\2\u01c2"+
"\u01a6\3\2\2\2\u01c2\u01ab\3\2\2\2\u01c2\u01b1\3\2\2\2\u01c2\u01b9\3\2"+
"\2\2\u01c2\u01c1\3\2\2\2\u01c3\u01cc\3\2\2\2\u01c4\u01c5\f\4\2\2\u01c5"+
"\u01c6\7\n\2\2\u01c6\u01cb\5.\30\5\u01c7\u01c8\f\3\2\2\u01c8\u01c9\7\63"+
"\2\2\u01c9\u01cb\5.\30\4\u01ca\u01c4\3\2\2\2\u01ca\u01c7\3\2\2\2\u01cb"+
"\u01ce\3\2\2\2\u01cc\u01ca\3\2\2\2\u01cc\u01cd\3\2\2\2\u01cd/\3\2\2\2"+
"\u01ce\u01cc\3\2\2\2\u01cf\u01d0\7\5\2\2\u01d0\u01d2\5b\62\2\u01d1\u01cf"+
"\3\2\2\2\u01d2\u01d5\3\2\2\2\u01d3\u01d1\3\2\2\2\u01d3\u01d4\3\2\2\2\u01d4"+
"\61\3\2\2\2\u01d5\u01d3\3\2\2\2\u01d6\u01d8\5<\37\2\u01d7\u01d9\5\64\33"+
"\2\u01d8\u01d7\3\2\2\2\u01d8\u01d9\3\2\2\2\u01d9\63\3\2\2\2\u01da\u01dc"+
"\7/\2\2\u01db\u01da\3\2\2\2\u01db\u01dc\3\2\2\2\u01dc\u01dd\3\2\2\2\u01dd"+
"\u01de\7\16\2\2\u01de\u01df\5<\37\2\u01df\u01e0\7\n\2\2\u01e0\u01e1\5"+
"<\37\2\u01e1\u0209\3\2\2\2\u01e2\u01e4\7/\2\2\u01e3\u01e2\3\2\2\2\u01e3"+
"\u01e4\3\2\2\2\u01e4\u01e5\3\2\2\2\u01e5\u01e6\7%\2\2\u01e6\u01e7\7\3"+
"\2\2\u01e7\u01ec\5,\27\2\u01e8\u01e9\7\5\2\2\u01e9\u01eb\5,\27\2\u01ea"+
"\u01e8\3\2\2\2\u01eb\u01ee\3\2\2\2\u01ec\u01ea\3\2\2\2\u01ec\u01ed\3\2"+
"\2\2\u01ed\u01ef\3\2\2\2\u01ee\u01ec\3\2\2\2\u01ef\u01f0\7\4\2\2\u01f0"+
"\u0209\3\2\2\2\u01f1\u01f3\7/\2\2\u01f2\u01f1\3\2\2\2\u01f2\u01f3\3\2"+
"\2\2\u01f3\u01f4\3\2\2\2\u01f4\u01f5\7%\2\2\u01f5\u01f6\7\3\2\2\u01f6"+
"\u01f7\5\b\5\2\u01f7\u01f8\7\4\2\2\u01f8\u0209\3\2\2\2\u01f9\u01fb\7/"+
"\2\2\u01fa\u01f9\3\2\2\2\u01fa\u01fb\3\2\2\2\u01fb\u01fc\3\2\2\2\u01fc"+
"\u01fd\7*\2\2\u01fd\u0209\58\35\2\u01fe\u0200\7/\2\2\u01ff\u01fe\3\2\2"+
"\2\u01ff\u0200\3\2\2\2\u0200\u0201\3\2\2\2\u0201\u0202\7:\2\2\u0202\u0209"+
"\5b\62\2\u0203\u0205\7\'\2\2\u0204\u0206\7/\2\2\u0205\u0204\3\2\2\2\u0205"+
"\u0206\3\2\2\2\u0206\u0207\3\2\2\2\u0207\u0209\7\60\2\2\u0208\u01db\3"+
"\2\2\2\u0208\u01e3\3\2\2\2\u0208\u01f2\3\2\2\2\u0208\u01fa\3\2\2\2\u0208"+
"\u01ff\3\2\2\2\u0208\u0203\3\2\2\2\u0209\65\3\2\2\2\u020a\u020b\7*\2\2"+
"\u020b\u020c\58\35\2\u020c\67\3\2\2\2\u020d\u020f\5b\62\2\u020e\u0210"+
"\5:\36\2\u020f\u020e\3\2\2\2\u020f\u0210\3\2\2\2\u02109\3\2\2\2\u0211"+
"\u0212\7\30\2\2\u0212\u0218\5b\62\2\u0213\u0214\7J\2\2\u0214\u0215\5b"+
"\62\2\u0215\u0216\7Q\2\2\u0216\u0218\3\2\2\2\u0217\u0211\3\2\2\2\u0217"+
"\u0213\3\2\2\2\u0218;\3\2\2\2\u0219\u021a\b\37\1\2\u021a\u021e\5> \2\u021b"+
"\u021c\t\n\2\2\u021c\u021e\5<\37\6\u021d\u0219\3\2\2\2\u021d\u021b\3\2"+
"\2\2\u021e\u022b\3\2\2\2\u021f\u0220\f\5\2\2\u0220\u0221\t\13\2\2\u0221"+
"\u022a\5<\37\6\u0222\u0223\f\4\2\2\u0223\u0224\t\n\2\2\u0224\u022a\5<"+
"\37\5\u0225\u0226\f\3\2\2\u0226\u0227\5P)\2\u0227\u0228\5<\37\4\u0228"+
"\u022a\3\2\2\2\u0229\u021f\3\2\2\2\u0229\u0222\3\2\2\2\u0229\u0225\3\2"+
"\2\2\u022a\u022d\3\2\2\2\u022b\u0229\3\2\2\2\u022b\u022c\3\2\2\2\u022c"+
"=\3\2\2\2\u022d\u022b\3\2\2\2\u022e\u0242\5@!\2\u022f\u0242\5D#\2\u0230"+
"\u0242\5N(\2\u0231\u0232\5V,\2\u0232\u0233\7^\2\2\u0233\u0235\3\2\2\2"+
"\u0234\u0231\3\2\2\2\u0234\u0235\3\2\2\2\u0235\u0236\3\2\2\2\u0236\u0242"+
"\7Z\2\2\u0237\u0242\5H%\2\u0238\u0239\7\3\2\2\u0239\u023a\5\b\5\2\u023a"+
"\u023b\7\4\2\2\u023b\u0242\3\2\2\2\u023c\u0242\5V,\2\u023d\u023e\7\3\2"+
"\2\u023e\u023f\5,\27\2\u023f\u0240\7\4\2\2\u0240\u0242\3\2\2\2\u0241\u022e"+
"\3\2\2\2\u0241\u022f\3\2\2\2\u0241\u0230\3\2\2\2\u0241\u0234\3\2\2\2\u0241"+
"\u0237\3\2\2\2\u0241\u0238\3\2\2\2\u0241\u023c\3\2\2\2\u0241\u023d\3\2"+
"\2\2\u0242?\3\2\2\2\u0243\u0249\5B\"\2\u0244\u0245\7K\2\2\u0245\u0246"+
"\5B\"\2\u0246\u0247\7Q\2\2\u0247\u0249\3\2\2\2\u0248\u0243\3\2\2\2\u0248"+
"\u0244\3\2\2\2\u0249A\3\2\2\2\u024a\u024b\7\20\2\2\u024b\u024c\7\3\2\2"+
"\u024c\u024d\5,\27\2\u024d\u024e\7\f\2\2\u024e\u024f\5T+\2\u024f\u0250"+
"\7\4\2\2\u0250C\3\2\2\2\u0251\u0257\5F$\2\u0252\u0253\7K\2\2\u0253\u0254"+
"\5F$\2\u0254\u0255\7Q\2\2\u0255\u0257\3\2\2\2\u0256\u0251\3\2\2\2\u0256"+
"\u0252\3\2\2\2\u0257E\3\2\2\2\u0258\u0259\7\34\2\2\u0259\u025a\7\3\2\2"+
"\u025a\u025b\5X-\2\u025b\u025c\7\37\2\2\u025c\u025d\5<\37\2\u025d\u025e"+
"\7\4\2\2\u025eG\3\2\2\2\u025f\u0265\5J&\2\u0260\u0261\7K\2\2\u0261\u0262"+
"\5J&\2\u0262\u0263\7Q\2\2\u0263\u0265\3\2\2\2\u0264\u025f\3\2\2\2\u0264"+
"\u0260\3\2\2\2\u0265I\3\2\2\2\u0266\u0267\5L\'\2\u0267\u0273\7\3\2\2\u0268"+
"\u026a\5\36\20\2\u0269\u0268\3\2\2\2\u0269\u026a\3\2\2\2\u026a\u026b\3"+
"\2\2\2\u026b\u0270\5,\27\2\u026c\u026d\7\5\2\2\u026d\u026f\5,\27\2\u026e"+
"\u026c\3\2\2\2\u026f\u0272\3\2\2\2\u0270\u026e\3\2\2\2\u0270\u0271\3\2"+
"\2\2\u0271\u0274\3\2\2\2\u0272\u0270\3\2\2\2\u0273\u0269\3\2\2\2\u0273"+
"\u0274\3\2\2\2\u0274\u0275\3\2\2\2\u0275\u0276\7\4\2\2\u0276K\3\2\2\2"+
"\u0277\u027b\7)\2\2\u0278\u027b\79\2\2\u0279\u027b\5X-\2\u027a\u0277\3"+
"\2\2\2\u027a\u0278\3\2\2\2\u027a\u0279\3\2\2\2\u027bM\3\2\2\2\u027c\u0296"+
"\7\60\2\2\u027d\u0296\5`\61\2\u027e\u0296\5R*\2\u027f\u0281\7`\2\2\u0280"+
"\u027f\3\2\2\2\u0281\u0282\3\2\2\2\u0282\u0280\3\2\2\2\u0282\u0283\3\2"+
"\2\2\u0283\u0296\3\2\2\2\u0284\u0296\7_\2\2\u0285\u0286\7M\2\2\u0286\u0287"+
"\5b\62\2\u0287\u0288\7Q\2\2\u0288\u0296\3\2\2\2\u0289\u028a\7N\2\2\u028a"+
"\u028b\5b\62\2\u028b\u028c\7Q\2\2\u028c\u0296\3\2\2\2\u028d\u028e\7O\2"+
"\2\u028e\u028f\5b\62\2\u028f\u0290\7Q\2\2\u0290\u0296\3\2\2\2\u0291\u0292"+
"\7P\2\2\u0292\u0293\5b\62\2\u0293\u0294\7Q\2\2\u0294\u0296\3\2\2\2\u0295"+
"\u027c\3\2\2\2\u0295\u027d\3\2\2\2\u0295\u027e\3\2\2\2\u0295\u0280\3\2"+
"\2\2\u0295\u0284\3\2\2\2\u0295\u0285\3\2\2\2\u0295\u0289\3\2\2\2\u0295"+
"\u028d\3\2\2\2\u0295\u0291\3\2\2\2\u0296O\3\2\2\2\u0297\u0298\t\f\2\2"+
"\u0298Q\3\2\2\2\u0299\u029a\t\r\2\2\u029aS\3\2\2\2\u029b\u029c\5X-\2\u029c"+
"U\3\2\2\2\u029d\u029e\5X-\2\u029e\u029f\7^\2\2\u029f\u02a1\3\2\2\2\u02a0"+
"\u029d\3\2\2\2\u02a1\u02a4\3\2\2\2\u02a2\u02a0\3\2\2\2\u02a2\u02a3\3\2"+
"\2\2\u02a3\u02a5\3\2\2\2\u02a4\u02a2\3\2\2\2\u02a5\u02a6\5X-\2\u02a6W"+
"\3\2\2\2\u02a7\u02aa\5\\/\2\u02a8\u02aa\5^\60\2\u02a9\u02a7\3\2\2\2\u02a9"+
"\u02a8\3\2\2\2\u02aaY\3\2\2\2\u02ab\u02ac\5X-\2\u02ac\u02ad\7\6\2\2\u02ad"+
"\u02af\3\2\2\2\u02ae\u02ab\3\2\2\2\u02ae\u02af\3\2\2\2\u02af\u02b0\3\2"+
"\2\2\u02b0\u02b8\7e\2\2\u02b1\u02b2\5X-\2\u02b2\u02b3\7\6\2\2\u02b3\u02b5"+
"\3\2\2\2\u02b4\u02b1\3\2\2\2\u02b4\u02b5\3\2\2\2\u02b5\u02b6\3\2\2\2\u02b6"+
"\u02b8\5X-\2\u02b7\u02ae\3\2\2\2\u02b7\u02b4\3\2\2\2\u02b8[\3\2\2\2\u02b9"+
"\u02bc\7f\2\2\u02ba\u02bc\7g\2\2\u02bb\u02b9\3\2\2\2\u02bb\u02ba\3\2\2"+
"\2\u02bc]\3\2\2\2\u02bd\u02c1\7c\2\2\u02be\u02c1\5d\63\2\u02bf\u02c1\7"+
"d\2\2\u02c0\u02bd\3\2\2\2\u02c0\u02be\3\2\2\2\u02c0\u02bf\3\2\2\2\u02c1"+
"_\3\2\2\2\u02c2\u02c4\t\n\2\2\u02c3\u02c2\3\2\2\2\u02c3\u02c4\3\2\2\2"+
"\u02c4\u02c5\3\2\2\2\u02c5\u02cb\7b\2\2\u02c6\u02c8\t\n\2\2\u02c7\u02c6"+
"\3\2\2\2\u02c7\u02c8\3\2\2\2\u02c8\u02c9\3\2\2\2\u02c9\u02cb\7a\2\2\u02ca"+
"\u02c3\3\2\2\2\u02ca\u02c7\3\2\2\2\u02cba\3\2\2\2\u02cc\u02cd\t\16\2\2"+
"\u02cdc\3\2\2\2\u02ce\u02cf\t\17\2\2\u02cfe\3\2\2\2buw{\u0084\u0086\u008a"+
"\u0091\u0098\u009d\u00a2\u00ac\u00b0\u00b8\u00bb\u00c1\u00c6\u00c9\u00d0"+
"\u00d8\u00db\u00e7\u00ea\u00ed\u00f4\u00fb\u00ff\u0103\u010a\u010e\u0112"+
"\u0117\u011b\u0123\u0127\u012e\u0139\u013c\u0140\u014c\u014f\u0155\u015c"+
"\u0163\u0166\u016a\u016e\u0172\u0174\u017f\u0184\u0188\u018b\u0191\u0194"+
"\u019a\u019d\u019f\u01c2\u01ca\u01cc\u01d3\u01d8\u01db\u01e3\u01ec\u01f2"+
"\u01fa\u01ff\u0205\u0208\u020f\u0217\u021d\u0229\u022b\u0234\u0241\u0248"+
"\u0256\u0264\u0269\u0270\u0273\u027a\u0282\u0295\u02a2\u02a9\u02ae\u02b4"+
"\u02b7\u02bb\u02c0\u02c3\u02c7\u02ca";
"\5/\u02bc\n/\3\60\3\60\3\60\5\60\u02c1\n\60\3\61\3\61\5\61\u02c5\n\61"+
"\3\62\3\62\3\63\3\63\3\63\2\4.<\64\2\4\6\b\n\f\16\20\22\24\26\30\32\34"+
"\36 \"$&(*,.\60\62\64\668:<>@BDFHJLNPRTVXZ\\^`bd\2\20\b\2\7\7\t\t\31\31"+
",,\62\62\66\66\4\2\"\"BB\4\2\t\t\62\62\4\2\37\37%%\3\2\25\26\4\2\7\7a"+
"a\4\2\r\r\25\25\4\2\7\7\27\27\3\2XY\3\2Z\\\3\2RW\4\2\35\35CC\3\2_`\20"+
"\2\b\t\22\24\31\31\33\33\36\36!\",,\62\62\668:<>?ABDEGG\u0320\2f\3\2\2"+
"\2\4i\3\2\2\2\6\u00d0\3\2\2\2\b\u00db\3\2\2\2\n\u00df\3\2\2\2\f\u00f4"+
"\3\2\2\2\16\u00fb\3\2\2\2\20\u00fd\3\2\2\2\22\u0101\3\2\2\2\24\u011d\3"+
"\2\2\2\26\u0127\3\2\2\2\30\u0131\3\2\2\2\32\u0140\3\2\2\2\34\u0142\3\2"+
"\2\2\36\u0148\3\2\2\2 \u014a\3\2\2\2\"\u0151\3\2\2\2$\u0163\3\2\2\2&\u0174"+
"\3\2\2\2(\u0184\3\2\2\2*\u019f\3\2\2\2,\u01a1\3\2\2\2.\u01c2\3\2\2\2\60"+
"\u01d3\3\2\2\2\62\u01d6\3\2\2\2\64\u0208\3\2\2\2\66\u020a\3\2\2\28\u020d"+
"\3\2\2\2:\u0217\3\2\2\2<\u021d\3\2\2\2>\u0241\3\2\2\2@\u0248\3\2\2\2B"+
"\u024a\3\2\2\2D\u0256\3\2\2\2F\u0258\3\2\2\2H\u0264\3\2\2\2J\u0266\3\2"+
"\2\2L\u027a\3\2\2\2N\u0295\3\2\2\2P\u0297\3\2\2\2R\u0299\3\2\2\2T\u029b"+
"\3\2\2\2V\u02a2\3\2\2\2X\u02a9\3\2\2\2Z\u02b7\3\2\2\2\\\u02bb\3\2\2\2"+
"^\u02c0\3\2\2\2`\u02c4\3\2\2\2b\u02c6\3\2\2\2d\u02c8\3\2\2\2fg\5\6\4\2"+
"gh\7\2\2\3h\3\3\2\2\2ij\5,\27\2jk\7\2\2\3k\5\3\2\2\2l\u00d1\5\b\5\2m{"+
"\7\33\2\2nw\7\3\2\2op\78\2\2pv\t\2\2\2qr\7\36\2\2rv\t\3\2\2st\7G\2\2t"+
"v\5R*\2uo\3\2\2\2uq\3\2\2\2us\3\2\2\2vy\3\2\2\2wu\3\2\2\2wx\3\2\2\2xz"+
"\3\2\2\2yw\3\2\2\2z|\7\4\2\2{n\3\2\2\2{|\3\2\2\2|}\3\2\2\2}\u00d1\5\6"+
"\4\2~\u008a\7\24\2\2\177\u0086\7\3\2\2\u0080\u0081\78\2\2\u0081\u0085"+
"\t\4\2\2\u0082\u0083\7\36\2\2\u0083\u0085\t\3\2\2\u0084\u0080\3\2\2\2"+
"\u0084\u0082\3\2\2\2\u0085\u0088\3\2\2\2\u0086\u0084\3\2\2\2\u0086\u0087"+
"\3\2\2\2\u0087\u0089\3\2\2\2\u0088\u0086\3\2\2\2\u0089\u008b\7\4\2\2\u008a"+
"\177\3\2\2\2\u008a\u008b\3\2\2\2\u008b\u008c\3\2\2\2\u008c\u00d1\5\6\4"+
"\2\u008d\u008e\7>\2\2\u008e\u0091\7A\2\2\u008f\u0092\5\66\34\2\u0090\u0092"+
"\5Z.\2\u0091\u008f\3\2\2\2\u0091\u0090\3\2\2\2\u0091\u0092\3\2\2\2\u0092"+
"\u00d1\3\2\2\2\u0093\u0094\7>\2\2\u0094\u0095\7\23\2\2\u0095\u0098\t\5"+
"\2\2\u0096\u0099\5\66\34\2\u0097\u0099\5Z.\2\u0098\u0096\3\2\2\2\u0098"+
"\u0097\3\2\2\2\u0099\u00d1\3\2\2\2\u009a\u009d\t\6\2\2\u009b\u009e\5\66"+
"\34\2\u009c\u009e\5Z.\2\u009d\u009b\3\2\2\2\u009d\u009c\3\2\2\2\u009e"+
"\u00d1\3\2\2\2\u009f\u00a0\7>\2\2\u00a0\u00a2\7!\2\2\u00a1\u00a3\5\66"+
"\34\2\u00a2\u00a1\3\2\2\2\u00a2\u00a3\3\2\2\2\u00a3\u00d1\3\2\2\2\u00a4"+
"\u00a5\7>\2\2\u00a5\u00d1\7<\2\2\u00a6\u00a7\7?\2\2\u00a7\u00d1\7\22\2"+
"\2\u00a8\u00a9\7?\2\2\u00a9\u00ac\7A\2\2\u00aa\u00ab\7\21\2\2\u00ab\u00ad"+
"\5\66\34\2\u00ac\u00aa\3\2\2\2\u00ac\u00ad\3\2\2\2\u00ad\u00b0\3\2\2\2"+
"\u00ae\u00b1\5\66\34\2\u00af\u00b1\5Z.\2\u00b0\u00ae\3\2\2\2\u00b0\u00af"+
"\3\2\2\2\u00b0\u00b1\3\2\2\2\u00b1\u00bb\3\2\2\2\u00b2\u00b3\7D\2\2\u00b3"+
"\u00b8\5b\62\2\u00b4\u00b5\7\5\2\2\u00b5\u00b7\5b\62\2\u00b6\u00b4\3\2"+
"\2\2\u00b7\u00ba\3\2\2\2\u00b8\u00b6\3\2\2\2\u00b8\u00b9\3\2\2\2\u00b9"+
"\u00bc\3\2\2\2\u00ba\u00b8\3\2\2\2\u00bb\u00b2\3\2\2\2\u00bb\u00bc\3\2"+
"\2\2\u00bc\u00d1\3\2\2\2\u00bd\u00be\7?\2\2\u00be\u00c1\7\23\2\2\u00bf"+
"\u00c0\7\21\2\2\u00c0\u00c2\5b\62\2\u00c1\u00bf\3\2\2\2\u00c1\u00c2\3"+
"\2\2\2\u00c2\u00c6\3\2\2\2\u00c3\u00c4\7@\2\2\u00c4\u00c7\5\66\34\2\u00c5"+
"\u00c7\5Z.\2\u00c6\u00c3\3\2\2\2\u00c6\u00c5\3\2\2\2\u00c6\u00c7\3\2\2"+
"\2\u00c7\u00c9\3\2\2\2\u00c8\u00ca\5\66\34\2\u00c9\u00c8\3\2\2\2\u00c9"+
"\u00ca\3\2\2\2\u00ca\u00d1\3\2\2\2\u00cb\u00cc\7?\2\2\u00cc\u00d1\7E\2"+
"\2\u00cd\u00ce\7?\2\2\u00ce\u00cf\7@\2\2\u00cf\u00d1\7E\2\2\u00d0l\3\2"+
"\2\2\u00d0m\3\2\2\2\u00d0~\3\2\2\2\u00d0\u008d\3\2\2\2\u00d0\u0093\3\2"+
"\2\2\u00d0\u009a\3\2\2\2\u00d0\u009f\3\2\2\2\u00d0\u00a4\3\2\2\2\u00d0"+
"\u00a6\3\2\2\2\u00d0\u00a8\3\2\2\2\u00d0\u00bd\3\2\2\2\u00d0\u00cb\3\2"+
"\2\2\u00d0\u00cd\3\2\2\2\u00d1\7\3\2\2\2\u00d2\u00d3\7I\2\2\u00d3\u00d8"+
"\5\34\17\2\u00d4\u00d5\7\5\2\2\u00d5\u00d7\5\34\17\2\u00d6\u00d4\3\2\2"+
"\2\u00d7\u00da\3\2\2\2\u00d8\u00d6\3\2\2\2\u00d8\u00d9\3\2\2\2\u00d9\u00dc"+
"\3\2\2\2\u00da\u00d8\3\2\2\2\u00db\u00d2\3\2\2\2\u00db\u00dc\3\2\2\2\u00dc"+
"\u00dd\3\2\2\2\u00dd\u00de\5\n\6\2\u00de\t\3\2\2\2\u00df\u00ea\5\16\b"+
"\2\u00e0\u00e1\7\64\2\2\u00e1\u00e2\7\17\2\2\u00e2\u00e7\5\20\t\2\u00e3"+
"\u00e4\7\5\2\2\u00e4\u00e6\5\20\t\2\u00e5\u00e3\3\2\2\2\u00e6\u00e9\3"+
"\2\2\2\u00e7\u00e5\3\2\2\2\u00e7\u00e8\3\2\2\2\u00e8\u00eb\3\2\2\2\u00e9"+
"\u00e7\3\2\2\2\u00ea\u00e0\3\2\2\2\u00ea\u00eb\3\2\2\2\u00eb\u00ed\3\2"+
"\2\2\u00ec\u00ee\5\f\7\2\u00ed\u00ec\3\2\2\2\u00ed\u00ee\3\2\2\2\u00ee"+
"\13\3\2\2\2\u00ef\u00f0\7+\2\2\u00f0\u00f5\t\7\2\2\u00f1\u00f2\7L\2\2"+
"\u00f2\u00f3\t\7\2\2\u00f3\u00f5\7Q\2\2\u00f4\u00ef\3\2\2\2\u00f4\u00f1"+
"\3\2\2\2\u00f5\r\3\2\2\2\u00f6\u00fc\5\22\n\2\u00f7\u00f8\7\3\2\2\u00f8"+
"\u00f9\5\n\6\2\u00f9\u00fa\7\4\2\2\u00fa\u00fc\3\2\2\2\u00fb\u00f6\3\2"+
"\2\2\u00fb\u00f7\3\2\2\2\u00fc\17\3\2\2\2\u00fd\u00ff\5,\27\2\u00fe\u0100"+
"\t\b\2\2\u00ff\u00fe\3\2\2\2\u00ff\u0100\3\2\2\2\u0100\21\3\2\2\2\u0101"+
"\u0103\7=\2\2\u0102\u0104\5\36\20\2\u0103\u0102\3\2\2\2\u0103\u0104\3"+
"\2\2\2\u0104\u0105\3\2\2\2\u0105\u010a\5 \21\2\u0106\u0107\7\5\2\2\u0107"+
"\u0109\5 \21\2\u0108\u0106\3\2\2\2\u0109\u010c\3\2\2\2\u010a\u0108\3\2"+
"\2\2\u010a\u010b\3\2\2\2\u010b\u010e\3\2\2\2\u010c\u010a\3\2\2\2\u010d"+
"\u010f\5\24\13\2\u010e\u010d\3\2\2\2\u010e\u010f\3\2\2\2\u010f\u0112\3"+
"\2\2\2\u0110\u0111\7H\2\2\u0111\u0113\5.\30\2\u0112\u0110\3\2\2\2\u0112"+
"\u0113\3\2\2\2\u0113\u0117\3\2\2\2\u0114\u0115\7#\2\2\u0115\u0116\7\17"+
"\2\2\u0116\u0118\5\26\f\2\u0117\u0114\3\2\2\2\u0117\u0118\3\2\2\2\u0118"+
"\u011b\3\2\2\2\u0119\u011a\7$\2\2\u011a\u011c\5.\30\2\u011b\u0119\3\2"+
"\2\2\u011b\u011c\3\2\2\2\u011c\23\3\2\2\2\u011d\u011e\7\37\2\2\u011e\u0123"+
"\5\"\22\2\u011f\u0120\7\5\2\2\u0120\u0122\5\"\22\2\u0121\u011f\3\2\2\2"+
"\u0122\u0125\3\2\2\2\u0123\u0121\3\2\2\2\u0123\u0124\3\2\2\2\u0124\25"+
"\3\2\2\2\u0125\u0123\3\2\2\2\u0126\u0128\5\36\20\2\u0127\u0126\3\2\2\2"+
"\u0127\u0128\3\2\2\2\u0128\u0129\3\2\2\2\u0129\u012e\5\30\r\2\u012a\u012b"+
"\7\5\2\2\u012b\u012d\5\30\r\2\u012c\u012a\3\2\2\2\u012d\u0130\3\2\2\2"+
"\u012e\u012c\3\2\2\2\u012e\u012f\3\2\2\2\u012f\27\3\2\2\2\u0130\u012e"+
"\3\2\2\2\u0131\u0132\5\32\16\2\u0132\31\3\2\2\2\u0133\u013c\7\3\2\2\u0134"+
"\u0139\5,\27\2\u0135\u0136\7\5\2\2\u0136\u0138\5,\27\2\u0137\u0135\3\2"+
"\2\2\u0138\u013b\3\2\2\2\u0139\u0137\3\2\2\2\u0139\u013a\3\2\2\2\u013a"+
"\u013d\3\2\2\2\u013b\u0139\3\2\2\2\u013c\u0134\3\2\2\2\u013c\u013d\3\2"+
"\2\2\u013d\u013e\3\2\2\2\u013e\u0141\7\4\2\2\u013f\u0141\5,\27\2\u0140"+
"\u0133\3\2\2\2\u0140\u013f\3\2\2\2\u0141\33\3\2\2\2\u0142\u0143\5X-\2"+
"\u0143\u0144\7\f\2\2\u0144\u0145\7\3\2\2\u0145\u0146\5\n\6\2\u0146\u0147"+
"\7\4\2\2\u0147\35\3\2\2\2\u0148\u0149\t\t\2\2\u0149\37\3\2\2\2\u014a\u014f"+
"\5,\27\2\u014b\u014d\7\f\2\2\u014c\u014b\3\2\2\2\u014c\u014d\3\2\2\2\u014d"+
"\u014e\3\2\2\2\u014e\u0150\5X-\2\u014f\u014c\3\2\2\2\u014f\u0150\3\2\2"+
"\2\u0150!\3\2\2\2\u0151\u0155\5*\26\2\u0152\u0154\5$\23\2\u0153\u0152"+
"\3\2\2\2\u0154\u0157\3\2\2\2\u0155\u0153\3\2\2\2\u0155\u0156\3\2\2\2\u0156"+
"#\3\2\2\2\u0157\u0155\3\2\2\2\u0158\u0159\5&\24\2\u0159\u015a\7(\2\2\u015a"+
"\u015c\5*\26\2\u015b\u015d\5(\25\2\u015c\u015b\3\2\2\2\u015c\u015d\3\2"+
"\2\2\u015d\u0164\3\2\2\2\u015e\u015f\7.\2\2\u015f\u0160\5&\24\2\u0160"+
"\u0161\7(\2\2\u0161\u0162\5*\26\2\u0162\u0164\3\2\2\2\u0163\u0158\3\2"+
"\2\2\u0163\u015e\3\2\2\2\u0164%\3\2\2\2\u0165\u0167\7&\2\2\u0166\u0165"+
"\3\2\2\2\u0166\u0167\3\2\2\2\u0167\u0175\3\2\2\2\u0168\u016a\7)\2\2\u0169"+
"\u016b\7\65\2\2\u016a\u0169\3\2\2\2\u016a\u016b\3\2\2\2\u016b\u0175\3"+
"\2\2\2\u016c\u016e\79\2\2\u016d\u016f\7\65\2\2\u016e\u016d\3\2\2\2\u016e"+
"\u016f\3\2\2\2\u016f\u0175\3\2\2\2\u0170\u0172\7 \2\2\u0171\u0173\7\65"+
"\2\2\u0172\u0171\3\2\2\2\u0172\u0173\3\2\2\2\u0173\u0175\3\2\2\2\u0174"+
"\u0166\3\2\2\2\u0174\u0168\3\2\2\2\u0174\u016c\3\2\2\2\u0174\u0170\3\2"+
"\2\2\u0175\'\3\2\2\2\u0176\u0177\7\61\2\2\u0177\u0185\5.\30\2\u0178\u0179"+
"\7F\2\2\u0179\u017a\7\3\2\2\u017a\u017f\5X-\2\u017b\u017c\7\5\2\2\u017c"+
"\u017e\5X-\2\u017d\u017b\3\2\2\2\u017e\u0181\3\2\2\2\u017f\u017d\3\2\2"+
"\2\u017f\u0180\3\2\2\2\u0180\u0182\3\2\2\2\u0181\u017f\3\2\2\2\u0182\u0183"+
"\7\4\2\2\u0183\u0185\3\2\2\2\u0184\u0176\3\2\2\2\u0184\u0178\3\2\2\2\u0185"+
")\3\2\2\2\u0186\u018b\5Z.\2\u0187\u0189\7\f\2\2\u0188\u0187\3\2\2\2\u0188"+
"\u0189\3\2\2\2\u0189\u018a\3\2\2\2\u018a\u018c\5V,\2\u018b\u0188\3\2\2"+
"\2\u018b\u018c\3\2\2\2\u018c\u01a0\3\2\2\2\u018d\u018e\7\3\2\2\u018e\u018f"+
"\5\n\6\2\u018f\u0194\7\4\2\2\u0190\u0192\7\f\2\2\u0191\u0190\3\2\2\2\u0191"+
"\u0192\3\2\2\2\u0192\u0193\3\2\2\2\u0193\u0195\5V,\2\u0194\u0191\3\2\2"+
"\2\u0194\u0195\3\2\2\2\u0195\u01a0\3\2\2\2\u0196\u0197\7\3\2\2\u0197\u0198"+
"\5\"\22\2\u0198\u019d\7\4\2\2\u0199\u019b\7\f\2\2\u019a\u0199\3\2\2\2"+
"\u019a\u019b\3\2\2\2\u019b\u019c\3\2\2\2\u019c\u019e\5V,\2\u019d\u019a"+
"\3\2\2\2\u019d\u019e\3\2\2\2\u019e\u01a0\3\2\2\2\u019f\u0186\3\2\2\2\u019f"+
"\u018d\3\2\2\2\u019f\u0196\3\2\2\2\u01a0+\3\2\2\2\u01a1\u01a2\5.\30\2"+
"\u01a2-\3\2\2\2\u01a3\u01a4\b\30\1\2\u01a4\u01a5\7/\2\2\u01a5\u01c3\5"+
".\30\n\u01a6\u01a7\7\32\2\2\u01a7\u01a8\7\3\2\2\u01a8\u01a9\5\b\5\2\u01a9"+
"\u01aa\7\4\2\2\u01aa\u01c3\3\2\2\2\u01ab\u01ac\7;\2\2\u01ac\u01ad\7\3"+
"\2\2\u01ad\u01ae\5b\62\2\u01ae\u01af\5\60\31\2\u01af\u01b0\7\4\2\2\u01b0"+
"\u01c3\3\2\2\2\u01b1\u01b2\7-\2\2\u01b2\u01b3\7\3\2\2\u01b3\u01b4\5V,"+
"\2\u01b4\u01b5\7\5\2\2\u01b5\u01b6\5b\62\2\u01b6\u01b7\5\60\31\2\u01b7"+
"\u01b8\7\4\2\2\u01b8\u01c3\3\2\2\2\u01b9\u01ba\7-\2\2\u01ba\u01bb\7\3"+
"\2\2\u01bb\u01bc\5b\62\2\u01bc\u01bd\7\5\2\2\u01bd\u01be\5b\62\2\u01be"+
"\u01bf\5\60\31\2\u01bf\u01c0\7\4\2\2\u01c0\u01c3\3\2\2\2\u01c1\u01c3\5"+
"\62\32\2\u01c2\u01a3\3\2\2\2\u01c2\u01a6\3\2\2\2\u01c2\u01ab\3\2\2\2\u01c2"+
"\u01b1\3\2\2\2\u01c2\u01b9\3\2\2\2\u01c2\u01c1\3\2\2\2\u01c3\u01cc\3\2"+
"\2\2\u01c4\u01c5\f\4\2\2\u01c5\u01c6\7\n\2\2\u01c6\u01cb\5.\30\5\u01c7"+
"\u01c8\f\3\2\2\u01c8\u01c9\7\63\2\2\u01c9\u01cb\5.\30\4\u01ca\u01c4\3"+
"\2\2\2\u01ca\u01c7\3\2\2\2\u01cb\u01ce\3\2\2\2\u01cc\u01ca\3\2\2\2\u01cc"+
"\u01cd\3\2\2\2\u01cd/\3\2\2\2\u01ce\u01cc\3\2\2\2\u01cf\u01d0\7\5\2\2"+
"\u01d0\u01d2\5b\62\2\u01d1\u01cf\3\2\2\2\u01d2\u01d5\3\2\2\2\u01d3\u01d1"+
"\3\2\2\2\u01d3\u01d4\3\2\2\2\u01d4\61\3\2\2\2\u01d5\u01d3\3\2\2\2\u01d6"+
"\u01d8\5<\37\2\u01d7\u01d9\5\64\33\2\u01d8\u01d7\3\2\2\2\u01d8\u01d9\3"+
"\2\2\2\u01d9\63\3\2\2\2\u01da\u01dc\7/\2\2\u01db\u01da\3\2\2\2\u01db\u01dc"+
"\3\2\2\2\u01dc\u01dd\3\2\2\2\u01dd\u01de\7\16\2\2\u01de\u01df\5<\37\2"+
"\u01df\u01e0\7\n\2\2\u01e0\u01e1\5<\37\2\u01e1\u0209\3\2\2\2\u01e2\u01e4"+
"\7/\2\2\u01e3\u01e2\3\2\2\2\u01e3\u01e4\3\2\2\2\u01e4\u01e5\3\2\2\2\u01e5"+
"\u01e6\7%\2\2\u01e6\u01e7\7\3\2\2\u01e7\u01ec\5,\27\2\u01e8\u01e9\7\5"+
"\2\2\u01e9\u01eb\5,\27\2\u01ea\u01e8\3\2\2\2\u01eb\u01ee\3\2\2\2\u01ec"+
"\u01ea\3\2\2\2\u01ec\u01ed\3\2\2\2\u01ed\u01ef\3\2\2\2\u01ee\u01ec\3\2"+
"\2\2\u01ef\u01f0\7\4\2\2\u01f0\u0209\3\2\2\2\u01f1\u01f3\7/\2\2\u01f2"+
"\u01f1\3\2\2\2\u01f2\u01f3\3\2\2\2\u01f3\u01f4\3\2\2\2\u01f4\u01f5\7%"+
"\2\2\u01f5\u01f6\7\3\2\2\u01f6\u01f7\5\b\5\2\u01f7\u01f8\7\4\2\2\u01f8"+
"\u0209\3\2\2\2\u01f9\u01fb\7/\2\2\u01fa\u01f9\3\2\2\2\u01fa\u01fb\3\2"+
"\2\2\u01fb\u01fc\3\2\2\2\u01fc\u01fd\7*\2\2\u01fd\u0209\58\35\2\u01fe"+
"\u0200\7/\2\2\u01ff\u01fe\3\2\2\2\u01ff\u0200\3\2\2\2\u0200\u0201\3\2"+
"\2\2\u0201\u0202\7:\2\2\u0202\u0209\5b\62\2\u0203\u0205\7\'\2\2\u0204"+
"\u0206\7/\2\2\u0205\u0204\3\2\2\2\u0205\u0206\3\2\2\2\u0206\u0207\3\2"+
"\2\2\u0207\u0209\7\60\2\2\u0208\u01db\3\2\2\2\u0208\u01e3\3\2\2\2\u0208"+
"\u01f2\3\2\2\2\u0208\u01fa\3\2\2\2\u0208\u01ff\3\2\2\2\u0208\u0203\3\2"+
"\2\2\u0209\65\3\2\2\2\u020a\u020b\7*\2\2\u020b\u020c\58\35\2\u020c\67"+
"\3\2\2\2\u020d\u020f\5b\62\2\u020e\u0210\5:\36\2\u020f\u020e\3\2\2\2\u020f"+
"\u0210\3\2\2\2\u02109\3\2\2\2\u0211\u0212\7\30\2\2\u0212\u0218\5b\62\2"+
"\u0213\u0214\7J\2\2\u0214\u0215\5b\62\2\u0215\u0216\7Q\2\2\u0216\u0218"+
"\3\2\2\2\u0217\u0211\3\2\2\2\u0217\u0213\3\2\2\2\u0218;\3\2\2\2\u0219"+
"\u021a\b\37\1\2\u021a\u021e\5> \2\u021b\u021c\t\n\2\2\u021c\u021e\5<\37"+
"\6\u021d\u0219\3\2\2\2\u021d\u021b\3\2\2\2\u021e\u022b\3\2\2\2\u021f\u0220"+
"\f\5\2\2\u0220\u0221\t\13\2\2\u0221\u022a\5<\37\6\u0222\u0223\f\4\2\2"+
"\u0223\u0224\t\n\2\2\u0224\u022a\5<\37\5\u0225\u0226\f\3\2\2\u0226\u0227"+
"\5P)\2\u0227\u0228\5<\37\4\u0228\u022a\3\2\2\2\u0229\u021f\3\2\2\2\u0229"+
"\u0222\3\2\2\2\u0229\u0225\3\2\2\2\u022a\u022d\3\2\2\2\u022b\u0229\3\2"+
"\2\2\u022b\u022c\3\2\2\2\u022c=\3\2\2\2\u022d\u022b\3\2\2\2\u022e\u0242"+
"\5@!\2\u022f\u0242\5D#\2\u0230\u0242\5N(\2\u0231\u0232\5V,\2\u0232\u0233"+
"\7^\2\2\u0233\u0235\3\2\2\2\u0234\u0231\3\2\2\2\u0234\u0235\3\2\2\2\u0235"+
"\u0236\3\2\2\2\u0236\u0242\7Z\2\2\u0237\u0242\5H%\2\u0238\u0239\7\3\2"+
"\2\u0239\u023a\5\b\5\2\u023a\u023b\7\4\2\2\u023b\u0242\3\2\2\2\u023c\u0242"+
"\5V,\2\u023d\u023e\7\3\2\2\u023e\u023f\5,\27\2\u023f\u0240\7\4\2\2\u0240"+
"\u0242\3\2\2\2\u0241\u022e\3\2\2\2\u0241\u022f\3\2\2\2\u0241\u0230\3\2"+
"\2\2\u0241\u0234\3\2\2\2\u0241\u0237\3\2\2\2\u0241\u0238\3\2\2\2\u0241"+
"\u023c\3\2\2\2\u0241\u023d\3\2\2\2\u0242?\3\2\2\2\u0243\u0249\5B\"\2\u0244"+
"\u0245\7K\2\2\u0245\u0246\5B\"\2\u0246\u0247\7Q\2\2\u0247\u0249\3\2\2"+
"\2\u0248\u0243\3\2\2\2\u0248\u0244\3\2\2\2\u0249A\3\2\2\2\u024a\u024b"+
"\7\20\2\2\u024b\u024c\7\3\2\2\u024c\u024d\5,\27\2\u024d\u024e\7\f\2\2"+
"\u024e\u024f\5T+\2\u024f\u0250\7\4\2\2\u0250C\3\2\2\2\u0251\u0257\5F$"+
"\2\u0252\u0253\7K\2\2\u0253\u0254\5F$\2\u0254\u0255\7Q\2\2\u0255\u0257"+
"\3\2\2\2\u0256\u0251\3\2\2\2\u0256\u0252\3\2\2\2\u0257E\3\2\2\2\u0258"+
"\u0259\7\34\2\2\u0259\u025a\7\3\2\2\u025a\u025b\5X-\2\u025b\u025c\7\37"+
"\2\2\u025c\u025d\5<\37\2\u025d\u025e\7\4\2\2\u025eG\3\2\2\2\u025f\u0265"+
"\5J&\2\u0260\u0261\7K\2\2\u0261\u0262\5J&\2\u0262\u0263\7Q\2\2\u0263\u0265"+
"\3\2\2\2\u0264\u025f\3\2\2\2\u0264\u0260\3\2\2\2\u0265I\3\2\2\2\u0266"+
"\u0267\5L\'\2\u0267\u0273\7\3\2\2\u0268\u026a\5\36\20\2\u0269\u0268\3"+
"\2\2\2\u0269\u026a\3\2\2\2\u026a\u026b\3\2\2\2\u026b\u0270\5,\27\2\u026c"+
"\u026d\7\5\2\2\u026d\u026f\5,\27\2\u026e\u026c\3\2\2\2\u026f\u0272\3\2"+
"\2\2\u0270\u026e\3\2\2\2\u0270\u0271\3\2\2\2\u0271\u0274\3\2\2\2\u0272"+
"\u0270\3\2\2\2\u0273\u0269\3\2\2\2\u0273\u0274\3\2\2\2\u0274\u0275\3\2"+
"\2\2\u0275\u0276\7\4\2\2\u0276K\3\2\2\2\u0277\u027b\7)\2\2\u0278\u027b"+
"\79\2\2\u0279\u027b\5X-\2\u027a\u0277\3\2\2\2\u027a\u0278\3\2\2\2\u027a"+
"\u0279\3\2\2\2\u027bM\3\2\2\2\u027c\u0296\7\60\2\2\u027d\u0296\5`\61\2"+
"\u027e\u0296\5R*\2\u027f\u0281\7`\2\2\u0280\u027f\3\2\2\2\u0281\u0282"+
"\3\2\2\2\u0282\u0280\3\2\2\2\u0282\u0283\3\2\2\2\u0283\u0296\3\2\2\2\u0284"+
"\u0296\7_\2\2\u0285\u0286\7M\2\2\u0286\u0287\5b\62\2\u0287\u0288\7Q\2"+
"\2\u0288\u0296\3\2\2\2\u0289\u028a\7N\2\2\u028a\u028b\5b\62\2\u028b\u028c"+
"\7Q\2\2\u028c\u0296\3\2\2\2\u028d\u028e\7O\2\2\u028e\u028f\5b\62\2\u028f"+
"\u0290\7Q\2\2\u0290\u0296\3\2\2\2\u0291\u0292\7P\2\2\u0292\u0293\5b\62"+
"\2\u0293\u0294\7Q\2\2\u0294\u0296\3\2\2\2\u0295\u027c\3\2\2\2\u0295\u027d"+
"\3\2\2\2\u0295\u027e\3\2\2\2\u0295\u0280\3\2\2\2\u0295\u0284\3\2\2\2\u0295"+
"\u0285\3\2\2\2\u0295\u0289\3\2\2\2\u0295\u028d\3\2\2\2\u0295\u0291\3\2"+
"\2\2\u0296O\3\2\2\2\u0297\u0298\t\f\2\2\u0298Q\3\2\2\2\u0299\u029a\t\r"+
"\2\2\u029aS\3\2\2\2\u029b\u029c\5X-\2\u029cU\3\2\2\2\u029d\u029e\5X-\2"+
"\u029e\u029f\7^\2\2\u029f\u02a1\3\2\2\2\u02a0\u029d\3\2\2\2\u02a1\u02a4"+
"\3\2\2\2\u02a2\u02a0\3\2\2\2\u02a2\u02a3\3\2\2\2\u02a3\u02a5\3\2\2\2\u02a4"+
"\u02a2\3\2\2\2\u02a5\u02a6\5X-\2\u02a6W\3\2\2\2\u02a7\u02aa\5\\/\2\u02a8"+
"\u02aa\5^\60\2\u02a9\u02a7\3\2\2\2\u02a9\u02a8\3\2\2\2\u02aaY\3\2\2\2"+
"\u02ab\u02ac\5X-\2\u02ac\u02ad\7\6\2\2\u02ad\u02af\3\2\2\2\u02ae\u02ab"+
"\3\2\2\2\u02ae\u02af\3\2\2\2\u02af\u02b0\3\2\2\2\u02b0\u02b8\7e\2\2\u02b1"+
"\u02b2\5X-\2\u02b2\u02b3\7\6\2\2\u02b3\u02b5\3\2\2\2\u02b4\u02b1\3\2\2"+
"\2\u02b4\u02b5\3\2\2\2\u02b5\u02b6\3\2\2\2\u02b6\u02b8\5X-\2\u02b7\u02ae"+
"\3\2\2\2\u02b7\u02b4\3\2\2\2\u02b8[\3\2\2\2\u02b9\u02bc\7f\2\2\u02ba\u02bc"+
"\7g\2\2\u02bb\u02b9\3\2\2\2\u02bb\u02ba\3\2\2\2\u02bc]\3\2\2\2\u02bd\u02c1"+
"\7c\2\2\u02be\u02c1\5d\63\2\u02bf\u02c1\7d\2\2\u02c0\u02bd\3\2\2\2\u02c0"+
"\u02be\3\2\2\2\u02c0\u02bf\3\2\2\2\u02c1_\3\2\2\2\u02c2\u02c5\7b\2\2\u02c3"+
"\u02c5\7a\2\2\u02c4\u02c2\3\2\2\2\u02c4\u02c3\3\2\2\2\u02c5a\3\2\2\2\u02c6"+
"\u02c7\t\16\2\2\u02c7c\3\2\2\2\u02c8\u02c9\t\17\2\2\u02c9e\3\2\2\2`uw"+
"{\u0084\u0086\u008a\u0091\u0098\u009d\u00a2\u00ac\u00b0\u00b8\u00bb\u00c1"+
"\u00c6\u00c9\u00d0\u00d8\u00db\u00e7\u00ea\u00ed\u00f4\u00fb\u00ff\u0103"+
"\u010a\u010e\u0112\u0117\u011b\u0123\u0127\u012e\u0139\u013c\u0140\u014c"+
"\u014f\u0155\u015c\u0163\u0166\u016a\u016e\u0172\u0174\u017f\u0184\u0188"+
"\u018b\u0191\u0194\u019a\u019d\u019f\u01c2\u01ca\u01cc\u01d3\u01d8\u01db"+
"\u01e3\u01ec\u01f2\u01fa\u01ff\u0205\u0208\u020f\u0217\u021d\u0229\u022b"+
"\u0234\u0241\u0248\u0256\u0264\u0269\u0270\u0273\u027a\u0282\u0295\u02a2"+
"\u02a9\u02ae\u02b4\u02b7\u02bb\u02c0\u02c4";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {

View File

@ -38,7 +38,7 @@ public class PreAnalyzerTests extends ESTestCase {
}
public void testWildIndexWithCatalog() {
LogicalPlan plan = parser.createStatement("SELECT * FROM elastic:index*");
LogicalPlan plan = parser.createStatement("SELECT * FROM elastic:\"index*\"");
PreAnalysis result = preAnalyzer.preAnalyze(plan);
assertThat(plan.preAnalyzed(), is(true));
assertThat(result.indices, hasSize(1));

View File

@ -6,31 +6,35 @@
package org.elasticsearch.xpack.sql.expression.function;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.LocationTests;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunction;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.parser.ParsingException;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.LocationTests;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;
import static java.util.Collections.emptyList;
import static org.elasticsearch.xpack.sql.expression.function.FunctionRegistry.def;
import static org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction.ResolutionType.DISTINCT;
import static org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction.ResolutionType.EXTRACT;
import static org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction.ResolutionType.STANDARD;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;
import static java.util.Collections.emptyList;
public class FunctionRegistryTests extends ESTestCase {
public void testNoArgFunction() {
UnresolvedFunction ur = uf(STANDARD);
FunctionRegistry r = new FunctionRegistry(Arrays.asList(def(Dummy.class, Dummy::new)));
FunctionRegistry r = new FunctionRegistry(Collections.singletonList(def(DummyFunction.class, DummyFunction::new)));
FunctionDefinition def = r.resolveFunction(ur.name());
assertEquals(ur.location(), ur.buildResolved(randomTimeZone(), def).location());
@ -47,9 +51,10 @@ public class FunctionRegistryTests extends ESTestCase {
public void testUnaryFunction() {
UnresolvedFunction ur = uf(STANDARD, mock(Expression.class));
FunctionRegistry r = new FunctionRegistry(Arrays.asList(def(Dummy.class, (Location l, Expression e) -> {
FunctionRegistry r = new FunctionRegistry(Collections.singletonList(
def(DummyFunction.class, (Location l, Expression e) -> {
assertSame(e, ur.children().get(0));
return new Dummy(l);
return new DummyFunction(l);
})));
FunctionDefinition def = r.resolveFunction(ur.name());
assertFalse(def.datetime());
@ -74,11 +79,12 @@ public class FunctionRegistryTests extends ESTestCase {
public void testUnaryDistinctAwareFunction() {
boolean urIsDistinct = randomBoolean();
UnresolvedFunction ur = uf(urIsDistinct ? DISTINCT : STANDARD, mock(Expression.class));
FunctionRegistry r = new FunctionRegistry(Arrays.asList(def(Dummy.class, (Location l, Expression e, boolean distinct) -> {
assertEquals(urIsDistinct, distinct);
assertSame(e, ur.children().get(0));
return new Dummy(l);
})));
FunctionRegistry r = new FunctionRegistry(Collections.singletonList(
def(DummyFunction.class, (Location l, Expression e, boolean distinct) -> {
assertEquals(urIsDistinct, distinct);
assertSame(e, ur.children().get(0));
return new DummyFunction(l);
})));
FunctionDefinition def = r.resolveFunction(ur.name());
assertEquals(ur.location(), ur.buildResolved(randomTimeZone(), def).location());
assertFalse(def.datetime());
@ -98,11 +104,12 @@ public class FunctionRegistryTests extends ESTestCase {
boolean urIsExtract = randomBoolean();
UnresolvedFunction ur = uf(urIsExtract ? EXTRACT : STANDARD, mock(Expression.class));
TimeZone providedTimeZone = randomTimeZone();
FunctionRegistry r = new FunctionRegistry(Arrays.asList(def(Dummy.class, (Location l, Expression e, TimeZone tz) -> {
assertEquals(providedTimeZone, tz);
assertSame(e, ur.children().get(0));
return new Dummy(l);
})));
FunctionRegistry r = new FunctionRegistry(Collections.singletonList(
def(DummyFunction.class, (Location l, Expression e, TimeZone tz) -> {
assertEquals(providedTimeZone, tz);
assertSame(e, ur.children().get(0));
return new DummyFunction(l);
})));
FunctionDefinition def = r.resolveFunction(ur.name());
assertEquals(ur.location(), ur.buildResolved(providedTimeZone, def).location());
assertTrue(def.datetime());
@ -125,11 +132,12 @@ public class FunctionRegistryTests extends ESTestCase {
public void testBinaryFunction() {
UnresolvedFunction ur = uf(STANDARD, mock(Expression.class), mock(Expression.class));
FunctionRegistry r = new FunctionRegistry(Arrays.asList(def(Dummy.class, (Location l, Expression lhs, Expression rhs) -> {
assertSame(lhs, ur.children().get(0));
assertSame(rhs, ur.children().get(1));
return new Dummy(l);
})));
FunctionRegistry r = new FunctionRegistry(Collections.singletonList(
def(DummyFunction.class, (Location l, Expression lhs, Expression rhs) -> {
assertSame(lhs, ur.children().get(0));
assertSame(rhs, ur.children().get(1));
return new DummyFunction(l);
})));
FunctionDefinition def = r.resolveFunction(ur.name());
assertEquals(ur.location(), ur.buildResolved(randomTimeZone(), def).location());
assertFalse(def.datetime());
@ -156,17 +164,60 @@ public class FunctionRegistryTests extends ESTestCase {
assertThat(e.getMessage(), endsWith("expects exactly two arguments"));
}
private UnresolvedFunction uf(UnresolvedFunction.ResolutionType resolutionType, Expression... children) {
return new UnresolvedFunction(LocationTests.randomLocation(), "dummy", resolutionType, Arrays.asList(children));
public void testFunctionResolving() {
UnresolvedFunction ur = uf(STANDARD, mock(Expression.class));
FunctionRegistry r = new FunctionRegistry(
Collections.singletonList(def(DummyFunction.class, (Location l, Expression e) -> {
assertSame(e, ur.children().get(0));
return new DummyFunction(l);
}, "DUMMY_FUNC")));
// Resolve by primary name
FunctionDefinition def = r.resolveFunction(r.resolveAlias("DuMMy_FuncTIon"));
assertEquals(ur.location(), ur.buildResolved(randomTimeZone(), def).location());
def = r.resolveFunction(r.resolveAlias("Dummy_Function"));
assertEquals(ur.location(), ur.buildResolved(randomTimeZone(), def).location());
def = r.resolveFunction(r.resolveAlias("dummy_function"));
assertEquals(ur.location(), ur.buildResolved(randomTimeZone(), def).location());
def = r.resolveFunction(r.resolveAlias("DUMMY_FUNCTION"));
assertEquals(ur.location(), ur.buildResolved(randomTimeZone(), def).location());
// Resolve by alias
def = r.resolveFunction(r.resolveAlias("DumMy_FunC"));
assertEquals(ur.location(), ur.buildResolved(randomTimeZone(), def).location());
def = r.resolveFunction(r.resolveAlias("dummy_func"));
assertEquals(ur.location(), ur.buildResolved(randomTimeZone(), def).location());
def = r.resolveFunction(r.resolveAlias("DUMMY_FUNC"));
assertEquals(ur.location(), ur.buildResolved(randomTimeZone(), def).location());
// Not resolved
SqlIllegalArgumentException e = expectThrows(SqlIllegalArgumentException.class,
() -> r.resolveFunction(r.resolveAlias("DummyFunction")));
assertThat(e.getMessage(),
is("Cannot find function DUMMYFUNCTION; this should have been caught during analysis"));
e = expectThrows(SqlIllegalArgumentException.class,
() -> r.resolveFunction(r.resolveAlias("dummyFunction")));
assertThat(e.getMessage(),
is("Cannot find function DUMMYFUNCTION; this should have been caught during analysis"));
}
public static class Dummy extends ScalarFunction {
public Dummy(Location location) {
private UnresolvedFunction uf(UnresolvedFunction.ResolutionType resolutionType, Expression... children) {
return new UnresolvedFunction(LocationTests.randomLocation(), "DUMMY_FUNCTION", resolutionType, Arrays.asList(children));
}
public static class DummyFunction extends ScalarFunction {
public DummyFunction(Location location) {
super(location, emptyList());
}
@Override
protected NodeInfo<Dummy> info() {
protected NodeInfo<DummyFunction> info() {
return NodeInfo.create(this);
}

View File

@ -9,21 +9,26 @@ import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.Literal;
import org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Add;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Mul;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Neg;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Sub;
import org.elasticsearch.xpack.sql.type.DataType;
import static org.hamcrest.core.StringStartsWith.startsWith;
public class ExpressionTests extends ESTestCase {
private final SqlParser parser = new SqlParser();
public void testTokenFunctionName() throws Exception {
public void testTokenFunctionName() {
Expression lt = parser.createExpression("LEFT()");
assertEquals(UnresolvedFunction.class, lt.getClass());
UnresolvedFunction uf = (UnresolvedFunction) lt;
assertEquals("LEFT", uf.functionName());
}
public void testLiteralBoolean() throws Exception {
public void testLiteralBoolean() {
Expression lt = parser.createExpression("TRUE");
assertEquals(Literal.class, lt.getClass());
Literal l = (Literal) lt;
@ -31,7 +36,7 @@ public class ExpressionTests extends ESTestCase {
assertEquals(DataType.BOOLEAN, l.dataType());
}
public void testLiteralDouble() throws Exception {
public void testLiteralDouble() {
Expression lt = parser.createExpression(String.valueOf(Double.MAX_VALUE));
assertEquals(Literal.class, lt.getClass());
Literal l = (Literal) lt;
@ -39,7 +44,7 @@ public class ExpressionTests extends ESTestCase {
assertEquals(DataType.DOUBLE, l.dataType());
}
public void testLiteralDoubleNegative() throws Exception {
public void testLiteralDoubleNegative() {
Expression lt = parser.createExpression(String.valueOf(Double.MIN_VALUE));
assertEquals(Literal.class, lt.getClass());
Literal l = (Literal) lt;
@ -47,7 +52,7 @@ public class ExpressionTests extends ESTestCase {
assertEquals(DataType.DOUBLE, l.dataType());
}
public void testLiteralDoublePositive() throws Exception {
public void testLiteralDoublePositive() {
Expression lt = parser.createExpression("+" + Double.MAX_VALUE);
assertEquals(Literal.class, lt.getClass());
Literal l = (Literal) lt;
@ -55,7 +60,7 @@ public class ExpressionTests extends ESTestCase {
assertEquals(DataType.DOUBLE, l.dataType());
}
public void testLiteralLong() throws Exception {
public void testLiteralLong() {
Expression lt = parser.createExpression(String.valueOf(Long.MAX_VALUE));
assertEquals(Literal.class, lt.getClass());
Literal l = (Literal) lt;
@ -63,14 +68,14 @@ public class ExpressionTests extends ESTestCase {
assertEquals(DataType.LONG, l.dataType());
}
public void testLiteralLongNegative() throws Exception {
public void testLiteralLongNegative() {
Expression lt = parser.createExpression(String.valueOf(Long.MIN_VALUE));
assertTrue(lt.foldable());
assertEquals(Long.MIN_VALUE, lt.fold());
assertEquals(DataType.LONG, lt.dataType());
}
public void testLiteralLongPositive() throws Exception {
public void testLiteralLongPositive() {
Expression lt = parser.createExpression("+" + String.valueOf(Long.MAX_VALUE));
assertEquals(Literal.class, lt.getClass());
Literal l = (Literal) lt;
@ -78,7 +83,7 @@ public class ExpressionTests extends ESTestCase {
assertEquals(DataType.LONG, l.dataType());
}
public void testLiteralInteger() throws Exception {
public void testLiteralInteger() {
Expression lt = parser.createExpression(String.valueOf(Integer.MAX_VALUE));
assertEquals(Literal.class, lt.getClass());
Literal l = (Literal) lt;
@ -86,29 +91,69 @@ public class ExpressionTests extends ESTestCase {
assertEquals(DataType.INTEGER, l.dataType());
}
public void testLiteralIntegerWithShortValue() throws Exception {
public void testLiteralIntegerWithShortValue() {
Expression lt = parser.createExpression(String.valueOf(Short.MAX_VALUE));
assertEquals(Literal.class, lt.getClass());
Literal l = (Literal) lt;
assertEquals(Integer.valueOf(Short.MAX_VALUE), l.value());
assertEquals((int) Short.MAX_VALUE, l.value());
assertEquals(DataType.INTEGER, l.dataType());
}
public void testLiteralIntegerWithByteValue() throws Exception {
public void testLiteralIntegerWithByteValue() {
Expression lt = parser.createExpression(String.valueOf(Byte.MAX_VALUE));
assertEquals(Literal.class, lt.getClass());
Literal l = (Literal) lt;
assertEquals(Integer.valueOf(Byte.MAX_VALUE), l.value());
assertEquals((int) Byte.MAX_VALUE, l.value());
assertEquals(DataType.INTEGER, l.dataType());
}
public void testLiteralIntegerInvalid() throws Exception {
public void testLiteralIntegerInvalid() {
ParsingException ex = expectThrows(ParsingException.class, () -> parser.createExpression("123456789098765432101"));
assertEquals("Number [123456789098765432101] is too large", ex.getErrorMessage());
}
public void testLiteralDecimalTooBig() throws Exception {
public void testLiteralDecimalTooBig() {
ParsingException ex = expectThrows(ParsingException.class, () -> parser.createExpression("1.9976931348623157e+308"));
assertEquals("Number [1.9976931348623157e+308] is too large", ex.getErrorMessage());
}
}
public void testLiteralTimesLiteral() {
Expression expr = parser.createExpression("10*2");
assertEquals(Mul.class, expr.getClass());
Mul mul = (Mul) expr;
assertEquals("10 * 2", mul.name());
assertEquals(DataType.INTEGER, mul.dataType());
}
public void testFunctionTimesLiteral() {
Expression expr = parser.createExpression("PI()*2");
assertEquals(Mul.class, expr.getClass());
Mul mul = (Mul) expr;
assertEquals("(PI) * 2", mul.name());
}
public void testComplexArithmetic() {
Expression expr = parser.createExpression("-(((a-2)-(-3))+b)");
assertEquals(Neg.class, expr.getClass());
Neg neg = (Neg) expr;
assertThat(neg.name(), startsWith("-(((a) - 2) - -3) + (b)#"));
assertEquals(1, neg.children().size());
assertEquals(Add.class, neg.children().get(0).getClass());
Add add = (Add) neg.children().get(0);
assertEquals("(((a) - 2) - -3) + (b)", add.name());
assertEquals(2, add.children().size());
assertEquals("?b", add.children().get(1).toString());
assertEquals(Sub.class, add.children().get(0).getClass());
Sub sub1 = (Sub) add.children().get(0);
assertEquals("((a) - 2) - -3", sub1.name());
assertEquals(2, sub1.children().size());
assertEquals(Literal.class, sub1.children().get(1).getClass());
assertEquals("-3", ((Literal) sub1.children().get(1)).name());
assertEquals(Sub.class, sub1.children().get(0).getClass());
Sub sub2 = (Sub) sub1.children().get(0);
assertEquals(2, sub2.children().size());
assertEquals("?a", sub2.children().get(0).toString());
assertEquals(Literal.class, sub2.children().get(1).getClass());
assertEquals("2", ((Literal) sub2.children().get(1)).name());
}
}

View File

@ -6,7 +6,6 @@
package org.elasticsearch.xpack.sql.tree;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.test.ESTestCase;
@ -418,7 +417,7 @@ public class NodeSubclassTests<T extends B, B extends Node<B>> extends ESTestCas
}
} else if (toBuildClass == ChildrenAreAProperty.class) {
/*
* While any subclass of Dummy will do here we want to prevent
* While any subclass of DummyFunction will do here we want to prevent
* stack overflow so we use the one without children.
*/
if (argClass == Dummy.class) {

View File

@ -288,6 +288,109 @@ setup:
- agg: "max"
- agg: "sum"
---
"Verify job caps by rollup index comma delimited list":
- skip:
version: " - 6.99.99"
reason: "comma delimited index support was fixed in 7.0"
- do:
headers:
Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser
xpack.rollup.put_job:
id: foo2
body: >
{
"index_pattern": "foo2",
"rollup_index": "foo_rollup",
"cron": "*/30 * * * * ?",
"page_size" :10,
"groups" : {
"date_histogram": {
"field": "the_field",
"interval": "1h"
}
},
"metrics": [
{
"field": "value_field",
"metrics": ["min", "max", "sum"]
}
]
}
- do:
headers:
Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser
xpack.rollup.put_job:
id: foo3
body: >
{
"index_pattern": "foo3",
"rollup_index": "foo_rollup2",
"cron": "*/30 * * * * ?",
"page_size" :10,
"groups" : {
"date_histogram": {
"field": "the_field",
"interval": "1h"
}
},
"metrics": [
{
"field": "value_field",
"metrics": ["min", "max", "sum"]
}
]
}
- do:
xpack.rollup.get_rollup_index_caps:
index: "foo_rollup2,foo_rollup"
- match:
$body:
foo_rollup:
rollup_jobs:
- job_id: "foo"
rollup_index: "foo_rollup"
index_pattern: "foo"
fields:
the_field:
- agg: "date_histogram"
interval: "1h"
time_zone: "UTC"
value_field:
- agg: "min"
- agg: "max"
- agg: "sum"
- job_id: "foo2"
rollup_index: "foo_rollup"
index_pattern: "foo2"
fields:
the_field:
- agg: "date_histogram"
interval: "1h"
time_zone: "UTC"
value_field:
- agg: "min"
- agg: "max"
- agg: "sum"
foo_rollup2:
rollup_jobs:
- job_id: "foo3"
rollup_index: "foo_rollup2"
index_pattern: "foo3"
fields:
the_field:
- agg: "date_histogram"
interval: "1h"
time_zone: "UTC"
value_field:
- agg: "min"
- agg: "max"
- agg: "sum"
---
"Verify index pattern":

View File

@ -47,7 +47,7 @@ salary | INTEGER
;
describePattern
DESCRIBE test_*;
DESCRIBE "test_*";
column:s | type:s
@ -99,7 +99,7 @@ F | 10099.28
;
testGroupByOnPattern
SELECT gender, PERCENTILE(emp_no, 97) p1 FROM test_* WHERE gender is NOT NULL GROUP BY gender;
SELECT gender, PERCENTILE(emp_no, 97) p1 FROM "test_*" WHERE gender is NOT NULL GROUP BY gender;
gender:s | p1:d