Backport of async search changes (#53976)
* Get Async Search: omit _clusters section when empty (#53907) The _clusters section is omitted by the search API whenever no remote clusters are searched. Async search should do the same, but Get Async Search returns a deserialized response, hence a weird `_clusters` section with all values set to `0` gets returned instead. In fact the recreated Clusters object is not the same object as the EMPTY constant, yet it has the same content. This commit addresses this by changing the comparison in the `toXContent` method to not print out the section if the number of total clusters is `0`. * Async search: remove version from response (#53960) The goal of the version field was to quickly show when you can expect to find something new in the search response, compared to when nothing has changed. This can also be done by looking at the `_shards` section and `num_reduce_phases` returned with the search response. In fact when there has been one or more additional reduction of the results, you can expect new results in the search response. Otherwise, the `_shards` section could notify of additional failures of shards that have completed the query, but that is not a guarantee that their results will be exposed (only when the following partial reduction is performed their results will be available). That said this commit clarifies this in the docs and removes the version field from the async search response * Async Search: replicas to auto expand from 0 to 1 (#53964) This way single node clusters that are green don't go yellow once async search is used, while all the others still have one replica. * [DOCS] address timing issue in async search docs tests (#53910) The docs snippets for submit async search have proven difficult to test as it is not possible to guarantee that you get a response that is not final, even when providing `wait_for_completion=0`. In the docs we want to show though a proper long-running query, and its first response should be partial rather than final. With this commit we adapt the docs snippets to show a partial response, and replace under the hood all that's needed to make the snippets tests succeed when we get a final response. Also, increased the timeout so we always get a final response. Closes #53887 Closes #53891
This commit is contained in:
parent
965af3a68b
commit
932a7e3112
|
@ -41,7 +41,6 @@ import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpect
|
||||||
public class AsyncSearchResponse implements ToXContentObject {
|
public class AsyncSearchResponse implements ToXContentObject {
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String id;
|
private final String id;
|
||||||
private final int version;
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final SearchResponse searchResponse;
|
private final SearchResponse searchResponse;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -55,15 +54,13 @@ public class AsyncSearchResponse implements ToXContentObject {
|
||||||
/**
|
/**
|
||||||
* Creates an {@link AsyncSearchResponse} with the arguments that are always present in the server response
|
* Creates an {@link AsyncSearchResponse} with the arguments that are always present in the server response
|
||||||
*/
|
*/
|
||||||
AsyncSearchResponse(int version,
|
AsyncSearchResponse(boolean isPartial,
|
||||||
boolean isPartial,
|
boolean isRunning,
|
||||||
boolean isRunning,
|
long startTimeMillis,
|
||||||
long startTimeMillis,
|
long expirationTimeMillis,
|
||||||
long expirationTimeMillis,
|
@Nullable String id,
|
||||||
@Nullable String id,
|
@Nullable SearchResponse searchResponse,
|
||||||
@Nullable SearchResponse searchResponse,
|
@Nullable ElasticsearchException error) {
|
||||||
@Nullable ElasticsearchException error) {
|
|
||||||
this.version = version;
|
|
||||||
this.isPartial = isPartial;
|
this.isPartial = isPartial;
|
||||||
this.isRunning = isRunning;
|
this.isRunning = isRunning;
|
||||||
this.startTimeMillis = startTimeMillis;
|
this.startTimeMillis = startTimeMillis;
|
||||||
|
@ -81,13 +78,6 @@ public class AsyncSearchResponse implements ToXContentObject {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the version of this response.
|
|
||||||
*/
|
|
||||||
public int getVersion() {
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current {@link SearchResponse} or <code>null</code> if not available.
|
* Returns the current {@link SearchResponse} or <code>null</code> if not available.
|
||||||
*
|
*
|
||||||
|
@ -145,7 +135,6 @@ public class AsyncSearchResponse implements ToXContentObject {
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
builder.field("id", id);
|
builder.field("id", id);
|
||||||
}
|
}
|
||||||
builder.field("version", version);
|
|
||||||
builder.field("is_partial", isPartial);
|
builder.field("is_partial", isPartial);
|
||||||
builder.field("is_running", isRunning);
|
builder.field("is_running", isRunning);
|
||||||
builder.field("start_time_in_millis", startTimeMillis);
|
builder.field("start_time_in_millis", startTimeMillis);
|
||||||
|
@ -165,7 +154,6 @@ public class AsyncSearchResponse implements ToXContentObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final ParseField ID_FIELD = new ParseField("id");
|
public static final ParseField ID_FIELD = new ParseField("id");
|
||||||
public static final ParseField VERSION_FIELD = new ParseField("version");
|
|
||||||
public static final ParseField IS_PARTIAL_FIELD = new ParseField("is_partial");
|
public static final ParseField IS_PARTIAL_FIELD = new ParseField("is_partial");
|
||||||
public static final ParseField IS_RUNNING_FIELD = new ParseField("is_running");
|
public static final ParseField IS_RUNNING_FIELD = new ParseField("is_running");
|
||||||
public static final ParseField START_TIME_FIELD = new ParseField("start_time_in_millis");
|
public static final ParseField START_TIME_FIELD = new ParseField("start_time_in_millis");
|
||||||
|
@ -176,16 +164,14 @@ public class AsyncSearchResponse implements ToXContentObject {
|
||||||
public static final ConstructingObjectParser<AsyncSearchResponse, Void> PARSER = new ConstructingObjectParser<>(
|
public static final ConstructingObjectParser<AsyncSearchResponse, Void> PARSER = new ConstructingObjectParser<>(
|
||||||
"submit_async_search_response", true,
|
"submit_async_search_response", true,
|
||||||
args -> new AsyncSearchResponse(
|
args -> new AsyncSearchResponse(
|
||||||
(int) args[0],
|
(boolean) args[0],
|
||||||
(boolean) args[1],
|
(boolean) args[1],
|
||||||
(boolean) args[2],
|
(long) args[2],
|
||||||
(long) args[3],
|
(long) args[3],
|
||||||
(long) args[4],
|
(String) args[4],
|
||||||
(String) args[5],
|
(SearchResponse) args[5],
|
||||||
(SearchResponse) args[6],
|
(ElasticsearchException) args[6]));
|
||||||
(ElasticsearchException) args[7]));
|
|
||||||
static {
|
static {
|
||||||
PARSER.declareInt(constructorArg(), VERSION_FIELD);
|
|
||||||
PARSER.declareBoolean(constructorArg(), IS_PARTIAL_FIELD);
|
PARSER.declareBoolean(constructorArg(), IS_PARTIAL_FIELD);
|
||||||
PARSER.declareBoolean(constructorArg(), IS_RUNNING_FIELD);
|
PARSER.declareBoolean(constructorArg(), IS_RUNNING_FIELD);
|
||||||
PARSER.declareLong(constructorArg(), START_TIME_FIELD);
|
PARSER.declareLong(constructorArg(), START_TIME_FIELD);
|
||||||
|
@ -203,7 +189,7 @@ public class AsyncSearchResponse implements ToXContentObject {
|
||||||
return SearchResponse.innerFromXContent(p);
|
return SearchResponse.innerFromXContent(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AsyncSearchResponse fromXContent(XContentParser parser) throws IOException {
|
public static AsyncSearchResponse fromXContent(XContentParser parser) {
|
||||||
return PARSER.apply(parser, null);
|
return PARSER.apply(parser, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,6 @@ public class AsyncSearchIT extends ESRestHighLevelClientTestCase {
|
||||||
// 15 sec should be enough to make sure we always complete right away
|
// 15 sec should be enough to make sure we always complete right away
|
||||||
request.setWaitForCompletion(new TimeValue(15, TimeUnit.SECONDS));
|
request.setWaitForCompletion(new TimeValue(15, TimeUnit.SECONDS));
|
||||||
AsyncSearchResponse response = highLevelClient().asyncSearch().submitAsyncSearch(request, RequestOptions.DEFAULT);
|
AsyncSearchResponse response = highLevelClient().asyncSearch().submitAsyncSearch(request, RequestOptions.DEFAULT);
|
||||||
assertTrue(response.getVersion() >= 0);
|
|
||||||
assertFalse(response.isPartial());
|
assertFalse(response.isPartial());
|
||||||
assertTrue(response.getStartTime() > 0);
|
assertTrue(response.getStartTime() > 0);
|
||||||
assertTrue(response.getExpirationTime() > 0);
|
assertTrue(response.getExpirationTime() > 0);
|
||||||
|
|
|
@ -36,7 +36,6 @@ public class AsyncSearchResponseTests
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected org.elasticsearch.xpack.core.search.action.AsyncSearchResponse createServerTestInstance(XContentType xContentType) {
|
protected org.elasticsearch.xpack.core.search.action.AsyncSearchResponse createServerTestInstance(XContentType xContentType) {
|
||||||
int version = randomIntBetween(0, Integer.MAX_VALUE);
|
|
||||||
boolean isPartial = randomBoolean();
|
boolean isPartial = randomBoolean();
|
||||||
boolean isRunning = randomBoolean();
|
boolean isRunning = randomBoolean();
|
||||||
long startTimeMillis = randomLongBetween(0, Long.MAX_VALUE);
|
long startTimeMillis = randomLongBetween(0, Long.MAX_VALUE);
|
||||||
|
@ -48,7 +47,7 @@ public class AsyncSearchResponseTests
|
||||||
: new SearchResponse(InternalSearchResponse.empty(), randomAlphaOfLength(10), 1, 1, 0, randomIntBetween(0, 10000),
|
: new SearchResponse(InternalSearchResponse.empty(), randomAlphaOfLength(10), 1, 1, 0, randomIntBetween(0, 10000),
|
||||||
ShardSearchFailure.EMPTY_ARRAY, Clusters.EMPTY);
|
ShardSearchFailure.EMPTY_ARRAY, Clusters.EMPTY);
|
||||||
org.elasticsearch.xpack.core.search.action.AsyncSearchResponse testResponse =
|
org.elasticsearch.xpack.core.search.action.AsyncSearchResponse testResponse =
|
||||||
new org.elasticsearch.xpack.core.search.action.AsyncSearchResponse(id, version, searchResponse, error, isPartial, isRunning,
|
new org.elasticsearch.xpack.core.search.action.AsyncSearchResponse(id, searchResponse, error, isPartial, isRunning,
|
||||||
startTimeMillis, expirationTimeMillis);
|
startTimeMillis, expirationTimeMillis);
|
||||||
return testResponse;
|
return testResponse;
|
||||||
}
|
}
|
||||||
|
@ -62,7 +61,6 @@ public class AsyncSearchResponseTests
|
||||||
protected void assertInstances(org.elasticsearch.xpack.core.search.action.AsyncSearchResponse expected, AsyncSearchResponse parsed) {
|
protected void assertInstances(org.elasticsearch.xpack.core.search.action.AsyncSearchResponse expected, AsyncSearchResponse parsed) {
|
||||||
assertNotSame(parsed, expected);
|
assertNotSame(parsed, expected);
|
||||||
assertEquals(expected.getId(), parsed.getId());
|
assertEquals(expected.getId(), parsed.getId());
|
||||||
assertEquals(expected.getVersion(), parsed.getVersion());
|
|
||||||
assertEquals(expected.isRunning(), parsed.isRunning());
|
assertEquals(expected.isRunning(), parsed.isRunning());
|
||||||
assertEquals(expected.isPartial(), parsed.isPartial());
|
assertEquals(expected.isPartial(), parsed.isPartial());
|
||||||
assertEquals(expected.getStartTime(), parsed.getStartTime());
|
assertEquals(expected.getStartTime(), parsed.getStartTime());
|
||||||
|
|
|
@ -30,9 +30,8 @@ POST /sales*/_async_search?size=0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
// TEST[skip:"AwaitsFix https://github.com/elastic/elasticsearch/issues/53891"]
|
|
||||||
// TEST[setup:sales]
|
// TEST[setup:sales]
|
||||||
// TEST[s/size=0/size=0&wait_for_completion=0/]
|
// TEST[s/size=0/size=0&wait_for_completion=10s&clean_on_completion=false/]
|
||||||
|
|
||||||
The response contains an identifier of the search being executed.
|
The response contains an identifier of the search being executed.
|
||||||
You can use this ID to later retrieve the search's final results.
|
You can use this ID to later retrieve the search's final results.
|
||||||
|
@ -43,7 +42,6 @@ results are returned as part of the <<search-api-response-body,`response`>> obje
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
{
|
{
|
||||||
"id" : "FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsdzoxMDc=", <1>
|
"id" : "FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsdzoxMDc=", <1>
|
||||||
"version" : 0,
|
|
||||||
"is_partial" : true, <2>
|
"is_partial" : true, <2>
|
||||||
"is_running" : true, <3>
|
"is_running" : true, <3>
|
||||||
"start_time_in_millis" : 1583945890986,
|
"start_time_in_millis" : 1583945890986,
|
||||||
|
@ -70,12 +68,17 @@ results are returned as part of the <<search-api-response-body,`response`>> obje
|
||||||
}
|
}
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
// TESTRESPONSE[s/FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsdzoxMDc=/$body.id/]
|
// TESTRESPONSE[s/FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsdzoxMDc=/$body.id/]
|
||||||
|
// TESTRESPONSE[s/"is_partial" : true/"is_partial": $body.is_partial/]
|
||||||
|
// TESTRESPONSE[s/"is_running" : true/"is_running": $body.is_running/]
|
||||||
// TESTRESPONSE[s/1583945890986/$body.start_time_in_millis/]
|
// TESTRESPONSE[s/1583945890986/$body.start_time_in_millis/]
|
||||||
// TESTRESPONSE[s/1584377890986/$body.expiration_time_in_millis/]
|
// TESTRESPONSE[s/1584377890986/$body.expiration_time_in_millis/]
|
||||||
// TESTRESPONSE[s/"took" : 1122/"took": $body.response.took/]
|
// TESTRESPONSE[s/"took" : 1122/"took": $body.response.took/]
|
||||||
|
// TESTRESPONSE[s/"num_reduce_phases" : 0,//]
|
||||||
// TESTRESPONSE[s/"total" : 562/"total": $body.response._shards.total/]
|
// TESTRESPONSE[s/"total" : 562/"total": $body.response._shards.total/]
|
||||||
// TESTRESPONSE[s/"successful" : 3/"successful": $body.response._shards.successful/]
|
// TESTRESPONSE[s/"successful" : 3/"successful": $body.response._shards.successful/]
|
||||||
// TESTRESPONSE[s/"value" : 157483/"value": $body.response.hits.total.value/]
|
// TESTRESPONSE[s/"value" : 157483/"value": $body.response.hits.total.value/]
|
||||||
|
// TESTRESPONSE[s/"relation" : "gte"/"relation": $body.response.hits.total.relation/]
|
||||||
|
// TESTRESPONSE[s/"hits" : \[ \]\n\s\s\s\s\}/"hits" : \[\]},"aggregations": $body.response.aggregations/]
|
||||||
|
|
||||||
<1> Identifier of the async search that can be used to monitor its progress, retrieve its results, and/or delete it.
|
<1> Identifier of the async search that can be used to monitor its progress, retrieve its results, and/or delete it.
|
||||||
<2> Whether the returned search results are partial or final
|
<2> Whether the returned search results are partial or final
|
||||||
|
@ -135,17 +138,16 @@ GET /_async_search/FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsd
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
{
|
{
|
||||||
"id" : "FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsdzoxMDc=",
|
"id" : "FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsdzoxMDc=",
|
||||||
"version" : 2, <1>
|
"is_partial" : true, <1>
|
||||||
"is_partial" : true, <2>
|
"is_running" : true, <2>
|
||||||
"is_running" : true, <3>
|
|
||||||
"start_time_in_millis" : 1583945890986,
|
"start_time_in_millis" : 1583945890986,
|
||||||
"expiration_time_in_millis" : 1584377890986, <4>
|
"expiration_time_in_millis" : 1584377890986, <3>
|
||||||
"response" : {
|
"response" : {
|
||||||
"took" : 12144,
|
"took" : 12144,
|
||||||
"timed_out" : false,
|
"timed_out" : false,
|
||||||
"num_reduce_phases" : 38,
|
"num_reduce_phases" : 46, <4>
|
||||||
"_shards" : {
|
"_shards" : {
|
||||||
"total" : 562,
|
"total" : 562, <5>
|
||||||
"successful" : 188,
|
"successful" : 188,
|
||||||
"skipped" : 0,
|
"skipped" : 0,
|
||||||
"failed" : 0
|
"failed" : 0
|
||||||
|
@ -158,7 +160,7 @@ GET /_async_search/FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsd
|
||||||
"max_score" : null,
|
"max_score" : null,
|
||||||
"hits" : [ ]
|
"hits" : [ ]
|
||||||
},
|
},
|
||||||
"aggregations" : { <5>
|
"aggregations" : { <6>
|
||||||
"sale_date" : {
|
"sale_date" : {
|
||||||
"buckets" : []
|
"buckets" : []
|
||||||
}
|
}
|
||||||
|
@ -176,17 +178,18 @@ GET /_async_search/FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsd
|
||||||
// TESTRESPONSE[s/"successful" : 188/"successful": $body.response._shards.successful/]
|
// TESTRESPONSE[s/"successful" : 188/"successful": $body.response._shards.successful/]
|
||||||
// TESTRESPONSE[s/"value" : 456433/"value": $body.response.hits.total.value/]
|
// TESTRESPONSE[s/"value" : 456433/"value": $body.response.hits.total.value/]
|
||||||
// TESTRESPONSE[s/"buckets" : \[\]/"buckets": $body.response.aggregations.sale_date.buckets/]
|
// TESTRESPONSE[s/"buckets" : \[\]/"buckets": $body.response.aggregations.sale_date.buckets/]
|
||||||
// TESTRESPONSE[s/"num_reduce_phases" : 38,//]
|
// TESTRESPONSE[s/"num_reduce_phases" : 46,//]
|
||||||
|
|
||||||
<1> The returned `version` is useful to identify whether the response contains
|
<1> Whether the returned search results are partial or final
|
||||||
additional results compared to previously obtained responses. If the version
|
<2> Whether the search is still being executed or it has completed
|
||||||
stays the same, no new results have become available, otherwise a higher version
|
<3> When the async search will expire
|
||||||
number indicates that more shards have completed their execution of the query
|
<4> Indicates how many reduction of the results have been performed. If this
|
||||||
and their partial results are also included in the response.
|
number increases compared to the last retrieved results, you can expect
|
||||||
<2> Whether the returned search results are partial or final
|
additional results included in the search response
|
||||||
<3> Whether the search is still being executed or it has completed
|
<5> Indicates how many shards have executed the query. Note that in order for
|
||||||
<4> When the async search will expire
|
shard results to be included in the search response, they need to be reduced
|
||||||
<5> Partial aggregations results, coming from the shards that have already
|
first.
|
||||||
|
<6> Partial aggregations results, coming from the shards that have already
|
||||||
completed the execution of the query.
|
completed the execution of the query.
|
||||||
|
|
||||||
The `wait_for_completion` parameter, which defaults to `1`, can also be provided
|
The `wait_for_completion` parameter, which defaults to `1`, can also be provided
|
||||||
|
|
|
@ -420,9 +420,7 @@ public class SearchResponse extends ActionResponse implements StatusToXContentOb
|
||||||
}
|
}
|
||||||
|
|
||||||
private Clusters(StreamInput in) throws IOException {
|
private Clusters(StreamInput in) throws IOException {
|
||||||
this.total = in.readVInt();
|
this(in.readVInt(), in.readVInt(), in.readVInt());
|
||||||
this.successful = in.readVInt();
|
|
||||||
this.skipped = in.readVInt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -434,7 +432,7 @@ public class SearchResponse extends ActionResponse implements StatusToXContentOb
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
if (this != EMPTY) {
|
if (total > 0) {
|
||||||
builder.startObject(_CLUSTERS_FIELD.getPreferredName());
|
builder.startObject(_CLUSTERS_FIELD.getPreferredName());
|
||||||
builder.field(TOTAL_FIELD.getPreferredName(), total);
|
builder.field(TOTAL_FIELD.getPreferredName(), total);
|
||||||
builder.field(SUCCESSFUL_FIELD.getPreferredName(), successful);
|
builder.field(SUCCESSFUL_FIELD.getPreferredName(), successful);
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.text.Text;
|
import org.elasticsearch.common.text.Text;
|
||||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
|
@ -292,4 +293,13 @@ public class SearchResponseTests extends ESTestCase {
|
||||||
assertEquals(searchResponse.getSkippedShards(), deserialized.getSkippedShards());
|
assertEquals(searchResponse.getSkippedShards(), deserialized.getSkippedShards());
|
||||||
assertEquals(searchResponse.getClusters(), deserialized.getClusters());
|
assertEquals(searchResponse.getClusters(), deserialized.getClusters());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testToXContentEmptyClusters() throws IOException {
|
||||||
|
SearchResponse searchResponse = new SearchResponse(InternalSearchResponse.empty(), null, 1, 1, 0, 1,
|
||||||
|
ShardSearchFailure.EMPTY_ARRAY, SearchResponse.Clusters.EMPTY);
|
||||||
|
SearchResponse deserialized = copyWriteable(searchResponse, namedWriteableRegistry, SearchResponse::new, Version.CURRENT);
|
||||||
|
XContentBuilder builder = XContentBuilder.builder(XContentType.JSON.xContent());
|
||||||
|
deserialized.getClusters().toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||||
|
assertEquals(0, Strings.toString(builder).length());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,14 +66,14 @@ class AsyncSearchIndexService {
|
||||||
public static final String EXPIRATION_TIME_FIELD = "expiration_time";
|
public static final String EXPIRATION_TIME_FIELD = "expiration_time";
|
||||||
public static final String RESULT_FIELD = "result";
|
public static final String RESULT_FIELD = "result";
|
||||||
|
|
||||||
public static Settings settings() {
|
private static Settings settings() {
|
||||||
return Settings.builder()
|
return Settings.builder()
|
||||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
.put(IndexMetaData.SETTING_AUTO_EXPAND_REPLICAS, "0-1")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static XContentBuilder mappings() throws IOException {
|
private static XContentBuilder mappings() throws IOException {
|
||||||
XContentBuilder builder = jsonBuilder()
|
XContentBuilder builder = jsonBuilder()
|
||||||
.startObject()
|
.startObject()
|
||||||
.startObject(SINGLE_MAPPING_NAME)
|
.startObject(SINGLE_MAPPING_NAME)
|
||||||
|
|
|
@ -40,7 +40,6 @@ class MutableSearchResponse {
|
||||||
private final AtomicArray<ShardSearchFailure> shardFailures;
|
private final AtomicArray<ShardSearchFailure> shardFailures;
|
||||||
private final Supplier<InternalAggregation.ReduceContext> aggReduceContextSupplier;
|
private final Supplier<InternalAggregation.ReduceContext> aggReduceContextSupplier;
|
||||||
|
|
||||||
private int version;
|
|
||||||
private boolean isPartial;
|
private boolean isPartial;
|
||||||
private boolean isFinalReduce;
|
private boolean isFinalReduce;
|
||||||
private int successfulShards;
|
private int successfulShards;
|
||||||
|
@ -63,7 +62,6 @@ class MutableSearchResponse {
|
||||||
this.skippedShards = skippedShards;
|
this.skippedShards = skippedShards;
|
||||||
this.clusters = clusters;
|
this.clusters = clusters;
|
||||||
this.aggReduceContextSupplier = aggReduceContextSupplier;
|
this.aggReduceContextSupplier = aggReduceContextSupplier;
|
||||||
this.version = 0;
|
|
||||||
this.shardFailures = totalShards == -1 ? null : new AtomicArray<>(totalShards-skippedShards);
|
this.shardFailures = totalShards == -1 ? null : new AtomicArray<>(totalShards-skippedShards);
|
||||||
this.isPartial = true;
|
this.isPartial = true;
|
||||||
this.sections = totalShards == -1 ? null : new InternalSearchResponse(
|
this.sections = totalShards == -1 ? null : new InternalSearchResponse(
|
||||||
|
@ -83,7 +81,6 @@ class MutableSearchResponse {
|
||||||
throw new IllegalStateException("received partial response out of order: "
|
throw new IllegalStateException("received partial response out of order: "
|
||||||
+ newSections.getNumReducePhases() + " < " + sections.getNumReducePhases());
|
+ newSections.getNumReducePhases() + " < " + sections.getNumReducePhases());
|
||||||
}
|
}
|
||||||
++ version;
|
|
||||||
this.successfulShards = successfulShards;
|
this.successfulShards = successfulShards;
|
||||||
this.sections = newSections;
|
this.sections = newSections;
|
||||||
this.isPartial = true;
|
this.isPartial = true;
|
||||||
|
@ -96,7 +93,6 @@ class MutableSearchResponse {
|
||||||
*/
|
*/
|
||||||
synchronized void updateFinalResponse(int successfulShards, SearchResponseSections newSections) {
|
synchronized void updateFinalResponse(int successfulShards, SearchResponseSections newSections) {
|
||||||
failIfFrozen();
|
failIfFrozen();
|
||||||
++ version;
|
|
||||||
this.successfulShards = successfulShards;
|
this.successfulShards = successfulShards;
|
||||||
this.sections = newSections;
|
this.sections = newSections;
|
||||||
this.isPartial = false;
|
this.isPartial = false;
|
||||||
|
@ -110,7 +106,6 @@ class MutableSearchResponse {
|
||||||
*/
|
*/
|
||||||
synchronized void updateWithFailure(Exception exc) {
|
synchronized void updateWithFailure(Exception exc) {
|
||||||
failIfFrozen();
|
failIfFrozen();
|
||||||
++ version;
|
|
||||||
this.isPartial = true;
|
this.isPartial = true;
|
||||||
this.failure = ElasticsearchException.guessRootCauses(exc)[0];
|
this.failure = ElasticsearchException.guessRootCauses(exc)[0];
|
||||||
this.frozen = true;
|
this.frozen = true;
|
||||||
|
@ -147,7 +142,7 @@ class MutableSearchResponse {
|
||||||
} else {
|
} else {
|
||||||
resp = null;
|
resp = null;
|
||||||
}
|
}
|
||||||
return new AsyncSearchResponse(task.getSearchId().getEncoded(), version, resp, failure, isPartial,
|
return new AsyncSearchResponse(task.getSearchId().getEncoded(), resp, failure, isPartial,
|
||||||
frozen == false, task.getStartTime(), expirationTime);
|
frozen == false, task.getStartTime(), expirationTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +154,7 @@ class MutableSearchResponse {
|
||||||
|
|
||||||
private ShardSearchFailure[] buildShardFailures() {
|
private ShardSearchFailure[] buildShardFailures() {
|
||||||
if (shardFailures == null) {
|
if (shardFailures == null) {
|
||||||
return new ShardSearchFailure[0];
|
return ShardSearchFailure.EMPTY_ARRAY;
|
||||||
}
|
}
|
||||||
List<ShardSearchFailure> failures = new ArrayList<>();
|
List<ShardSearchFailure> failures = new ArrayList<>();
|
||||||
for (int i = 0; i < shardFailures.length(); i++) {
|
for (int i = 0; i < shardFailures.length(); i++) {
|
||||||
|
|
|
@ -5,6 +5,10 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.search;
|
package org.elasticsearch.xpack.search;
|
||||||
|
|
||||||
|
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
|
||||||
|
import org.elasticsearch.action.admin.indices.get.GetIndexResponse;
|
||||||
|
import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
|
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||||
import org.elasticsearch.cluster.service.ClusterService;
|
import org.elasticsearch.cluster.service.ClusterService;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
|
@ -17,6 +21,7 @@ import org.junit.Before;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import static org.elasticsearch.xpack.search.AsyncSearchResponseTests.assertEqualResponses;
|
import static org.elasticsearch.xpack.search.AsyncSearchResponseTests.assertEqualResponses;
|
||||||
import static org.elasticsearch.xpack.search.AsyncSearchResponseTests.randomAsyncSearchResponse;
|
import static org.elasticsearch.xpack.search.AsyncSearchResponseTests.randomAsyncSearchResponse;
|
||||||
|
@ -100,4 +105,15 @@ public class AsyncSearchIndexServiceTests extends ESSingleNodeTestCase {
|
||||||
assertFalse(indexService.ensureAuthenticatedUserIsSame(original, runAsDiffType));
|
assertFalse(indexService.ensureAuthenticatedUserIsSame(original, runAsDiffType));
|
||||||
assertFalse(indexService.ensureAuthenticatedUserIsSame(threadContext.getHeaders(), runAsDiffType));
|
assertFalse(indexService.ensureAuthenticatedUserIsSame(threadContext.getHeaders(), runAsDiffType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testSettings() throws ExecutionException, InterruptedException {
|
||||||
|
PlainActionFuture<Void> future = PlainActionFuture.newFuture();
|
||||||
|
indexService.createIndexIfNecessary(future);
|
||||||
|
future.get();
|
||||||
|
GetIndexResponse getIndexResponse = client().admin().indices().getIndex(
|
||||||
|
new GetIndexRequest().indices(AsyncSearchIndexService.INDEX)).actionGet();
|
||||||
|
Settings settings = getIndexResponse.getSettings().get(AsyncSearchIndexService.INDEX);
|
||||||
|
assertEquals("1", settings.get(IndexMetaData.SETTING_NUMBER_OF_SHARDS));
|
||||||
|
assertEquals("0-1", settings.get(IndexMetaData.SETTING_AUTO_EXPAND_REPLICAS));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,15 +91,15 @@ public class AsyncSearchResponseTests extends ESTestCase {
|
||||||
int rand = randomIntBetween(0, 2);
|
int rand = randomIntBetween(0, 2);
|
||||||
switch (rand) {
|
switch (rand) {
|
||||||
case 0:
|
case 0:
|
||||||
return new AsyncSearchResponse(searchId, randomIntBetween(0, Integer.MAX_VALUE), randomBoolean(),
|
return new AsyncSearchResponse(searchId, randomBoolean(),
|
||||||
randomBoolean(), randomNonNegativeLong(), randomNonNegativeLong());
|
randomBoolean(), randomNonNegativeLong(), randomNonNegativeLong());
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
return new AsyncSearchResponse(searchId, randomIntBetween(0, Integer.MAX_VALUE), searchResponse, null,
|
return new AsyncSearchResponse(searchId, searchResponse, null,
|
||||||
randomBoolean(), randomBoolean(), randomNonNegativeLong(), randomNonNegativeLong());
|
randomBoolean(), randomBoolean(), randomNonNegativeLong(), randomNonNegativeLong());
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
return new AsyncSearchResponse(searchId, randomIntBetween(0, Integer.MAX_VALUE), searchResponse,
|
return new AsyncSearchResponse(searchId, searchResponse,
|
||||||
new ElasticsearchException(new IOException("boum")), randomBoolean(), randomBoolean(),
|
new ElasticsearchException(new IOException("boum")), randomBoolean(), randomBoolean(),
|
||||||
randomNonNegativeLong(), randomNonNegativeLong());
|
randomNonNegativeLong(), randomNonNegativeLong());
|
||||||
|
|
||||||
|
@ -120,7 +120,6 @@ public class AsyncSearchResponseTests extends ESTestCase {
|
||||||
|
|
||||||
static void assertEqualResponses(AsyncSearchResponse expected, AsyncSearchResponse actual) {
|
static void assertEqualResponses(AsyncSearchResponse expected, AsyncSearchResponse actual) {
|
||||||
assertEquals(expected.getId(), actual.getId());
|
assertEquals(expected.getId(), actual.getId());
|
||||||
assertEquals(expected.getVersion(), actual.getVersion());
|
|
||||||
assertEquals(expected.status(), actual.status());
|
assertEquals(expected.status(), actual.status());
|
||||||
assertEquals(expected.getFailure() == null, actual.getFailure() == null);
|
assertEquals(expected.getFailure() == null, actual.getFailure() == null);
|
||||||
assertEquals(expected.isRunning(), actual.isRunning());
|
assertEquals(expected.isRunning(), actual.isRunning());
|
||||||
|
|
|
@ -25,7 +25,6 @@ import static org.elasticsearch.rest.RestStatus.OK;
|
||||||
public class AsyncSearchResponse extends ActionResponse implements StatusToXContentObject {
|
public class AsyncSearchResponse extends ActionResponse implements StatusToXContentObject {
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String id;
|
private final String id;
|
||||||
private final int version;
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final SearchResponse searchResponse;
|
private final SearchResponse searchResponse;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -40,19 +39,17 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
|
||||||
* Creates an {@link AsyncSearchResponse} with meta-information only (not-modified).
|
* Creates an {@link AsyncSearchResponse} with meta-information only (not-modified).
|
||||||
*/
|
*/
|
||||||
public AsyncSearchResponse(String id,
|
public AsyncSearchResponse(String id,
|
||||||
int version,
|
|
||||||
boolean isPartial,
|
boolean isPartial,
|
||||||
boolean isRunning,
|
boolean isRunning,
|
||||||
long startTimeMillis,
|
long startTimeMillis,
|
||||||
long expirationTimeMillis) {
|
long expirationTimeMillis) {
|
||||||
this(id, version, null, null, isPartial, isRunning, startTimeMillis, expirationTimeMillis);
|
this(id, null, null, isPartial, isRunning, startTimeMillis, expirationTimeMillis);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link AsyncSearchResponse}
|
* Creates a new {@link AsyncSearchResponse}
|
||||||
*
|
*
|
||||||
* @param id The id of the search for further retrieval, <code>null</code> if not stored.
|
* @param id The id of the search for further retrieval, <code>null</code> if not stored.
|
||||||
* @param version The version number of this response.
|
|
||||||
* @param searchResponse The actual search response.
|
* @param searchResponse The actual search response.
|
||||||
* @param error The error if the search failed, <code>null</code> if the search is running
|
* @param error The error if the search failed, <code>null</code> if the search is running
|
||||||
* or has completed without failure.
|
* or has completed without failure.
|
||||||
|
@ -61,7 +58,6 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
|
||||||
* @param startTimeMillis The start date of the search in milliseconds since epoch.
|
* @param startTimeMillis The start date of the search in milliseconds since epoch.
|
||||||
*/
|
*/
|
||||||
public AsyncSearchResponse(String id,
|
public AsyncSearchResponse(String id,
|
||||||
int version,
|
|
||||||
SearchResponse searchResponse,
|
SearchResponse searchResponse,
|
||||||
ElasticsearchException error,
|
ElasticsearchException error,
|
||||||
boolean isPartial,
|
boolean isPartial,
|
||||||
|
@ -69,7 +65,6 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
|
||||||
long startTimeMillis,
|
long startTimeMillis,
|
||||||
long expirationTimeMillis) {
|
long expirationTimeMillis) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.version = version;
|
|
||||||
this.error = error;
|
this.error = error;
|
||||||
this.searchResponse = searchResponse;
|
this.searchResponse = searchResponse;
|
||||||
this.isPartial = isPartial;
|
this.isPartial = isPartial;
|
||||||
|
@ -80,7 +75,6 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
|
||||||
|
|
||||||
public AsyncSearchResponse(StreamInput in) throws IOException {
|
public AsyncSearchResponse(StreamInput in) throws IOException {
|
||||||
this.id = in.readOptionalString();
|
this.id = in.readOptionalString();
|
||||||
this.version = in.readVInt();
|
|
||||||
this.error = in.readOptionalWriteable(ElasticsearchException::new);
|
this.error = in.readOptionalWriteable(ElasticsearchException::new);
|
||||||
this.searchResponse = in.readOptionalWriteable(SearchResponse::new);
|
this.searchResponse = in.readOptionalWriteable(SearchResponse::new);
|
||||||
this.isPartial = in.readBoolean();
|
this.isPartial = in.readBoolean();
|
||||||
|
@ -92,7 +86,6 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
out.writeOptionalString(id);
|
out.writeOptionalString(id);
|
||||||
out.writeVInt(version);
|
|
||||||
out.writeOptionalWriteable(error);
|
out.writeOptionalWriteable(error);
|
||||||
out.writeOptionalWriteable(searchResponse);
|
out.writeOptionalWriteable(searchResponse);
|
||||||
out.writeBoolean(isPartial);
|
out.writeBoolean(isPartial);
|
||||||
|
@ -102,7 +95,7 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
|
||||||
}
|
}
|
||||||
|
|
||||||
public AsyncSearchResponse clone(String id) {
|
public AsyncSearchResponse clone(String id) {
|
||||||
return new AsyncSearchResponse(id, version, searchResponse, error, isPartial, false, startTimeMillis, expirationTimeMillis);
|
return new AsyncSearchResponse(id, searchResponse, error, isPartial, false, startTimeMillis, expirationTimeMillis);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -113,13 +106,6 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the version of this response.
|
|
||||||
*/
|
|
||||||
public int getVersion() {
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current {@link SearchResponse} or <code>null</code> if not available.
|
* Returns the current {@link SearchResponse} or <code>null</code> if not available.
|
||||||
*
|
*
|
||||||
|
@ -189,7 +175,6 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
builder.field("id", id);
|
builder.field("id", id);
|
||||||
}
|
}
|
||||||
builder.field("version", version);
|
|
||||||
builder.field("is_partial", isPartial);
|
builder.field("is_partial", isPartial);
|
||||||
builder.field("is_running", isRunning);
|
builder.field("is_running", isRunning);
|
||||||
builder.field("start_time_in_millis", startTimeMillis);
|
builder.field("start_time_in_millis", startTimeMillis);
|
||||||
|
|
|
@ -54,7 +54,6 @@
|
||||||
sort: max
|
sort: max
|
||||||
|
|
||||||
- is_false: id
|
- is_false: id
|
||||||
- match: { version: 6 }
|
|
||||||
- match: { is_partial: false }
|
- match: { is_partial: false }
|
||||||
- length: { response.hits.hits: 3 }
|
- length: { response.hits.hits: 3 }
|
||||||
- match: { response.hits.hits.0._source.max: 1 }
|
- match: { response.hits.hits.0._source.max: 1 }
|
||||||
|
@ -73,8 +72,6 @@
|
||||||
field: max
|
field: max
|
||||||
sort: max
|
sort: max
|
||||||
|
|
||||||
- set: { id: id }
|
|
||||||
- match: { version: 6 }
|
|
||||||
- match: { is_partial: false }
|
- match: { is_partial: false }
|
||||||
- length: { response.hits.hits: 3 }
|
- length: { response.hits.hits: 3 }
|
||||||
- match: { response.hits.hits.0._source.max: 1 }
|
- match: { response.hits.hits.0._source.max: 1 }
|
||||||
|
@ -96,8 +93,8 @@
|
||||||
sort: max
|
sort: max
|
||||||
|
|
||||||
- set: { id: id }
|
- set: { id: id }
|
||||||
- match: { version: 6 }
|
|
||||||
- match: { is_partial: false }
|
- match: { is_partial: false }
|
||||||
|
- is_false: response._clusters
|
||||||
- length: { response.hits.hits: 3 }
|
- length: { response.hits.hits: 3 }
|
||||||
- match: { response.hits.hits.0._source.max: 1 }
|
- match: { response.hits.hits.0._source.max: 1 }
|
||||||
- match: { response.aggregations.max#max.value: 3.0 }
|
- match: { response.aggregations.max#max.value: 3.0 }
|
||||||
|
@ -106,8 +103,8 @@
|
||||||
async_search.get:
|
async_search.get:
|
||||||
id: "$id"
|
id: "$id"
|
||||||
|
|
||||||
- match: { version: 6 }
|
|
||||||
- match: { is_partial: false }
|
- match: { is_partial: false }
|
||||||
|
- is_false: response._clusters
|
||||||
- length: { response.hits.hits: 3 }
|
- length: { response.hits.hits: 3 }
|
||||||
- match: { response.hits.hits.0._source.max: 1 }
|
- match: { response.hits.hits.0._source.max: 1 }
|
||||||
- match: { response.aggregations.max.value: 3.0 }
|
- match: { response.aggregations.max.value: 3.0 }
|
||||||
|
@ -118,8 +115,8 @@
|
||||||
id: "$id"
|
id: "$id"
|
||||||
typed_keys: true
|
typed_keys: true
|
||||||
|
|
||||||
- match: { version: 6 }
|
|
||||||
- match: { is_partial: false }
|
- match: { is_partial: false }
|
||||||
|
- is_false: response._clusters
|
||||||
- length: { response.hits.hits: 3 }
|
- length: { response.hits.hits: 3 }
|
||||||
- match: { response.hits.hits.0._source.max: 1 }
|
- match: { response.hits.hits.0._source.max: 1 }
|
||||||
- match: { response.aggregations.max#max.value: 3.0 }
|
- match: { response.aggregations.max#max.value: 3.0 }
|
||||||
|
|
Loading…
Reference in New Issue