Skip to content

Commit 86b87a5

Browse files
authored
[Backport master] Implement AsyncSearch Status API (#5269)
* Implement AsyncSearch Status API (#5239) * Fixup
1 parent 9cd87cd commit 86b87a5

18 files changed

+225
-7
lines changed

src/Elasticsearch.Net/Api/RequestParameters/RequestParameters.AsyncSearch.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public TimeSpan WaitForCompletionTimeout
5656
}
5757

5858
///<summary>Request options for Status <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
59-
public class StatusRequestParameters : RequestParameters<StatusRequestParameters>
59+
public class AsyncSearchStatusRequestParameters : RequestParameters<AsyncSearchStatusRequestParameters>
6060
{
6161
}
6262

src/Elasticsearch.Net/ElasticLowLevelClient.AsyncSearch.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,13 @@ public Task<TResponse> GetAsync<TResponse>(string id, AsyncSearchGetRequestParam
6969
///<summary>GET on /_async_search/status/{id} <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
7070
///<param name = "id">The async search ID</param>
7171
///<param name = "requestParameters">Request specific configuration such as querystring parameters &amp; request specific connection settings.</param>
72-
public TResponse Status<TResponse>(string id, StatusRequestParameters requestParameters = null)
72+
public TResponse Status<TResponse>(string id, AsyncSearchStatusRequestParameters requestParameters = null)
7373
where TResponse : class, ITransportResponse, new() => DoRequest<TResponse>(GET, Url($"_async_search/status/{id:id}"), null, RequestParams(requestParameters));
7474
///<summary>GET on /_async_search/status/{id} <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
7575
///<param name = "id">The async search ID</param>
7676
///<param name = "requestParameters">Request specific configuration such as querystring parameters &amp; request specific connection settings.</param>
7777
[MapsApi("async_search.status", "id")]
78-
public Task<TResponse> StatusAsync<TResponse>(string id, StatusRequestParameters requestParameters = null, CancellationToken ctx = default)
78+
public Task<TResponse> StatusAsync<TResponse>(string id, AsyncSearchStatusRequestParameters requestParameters = null, CancellationToken ctx = default)
7979
where TResponse : class, ITransportResponse, new() => DoRequestAsync<TResponse>(GET, Url($"_async_search/status/{id:id}"), ctx, null, RequestParams(requestParameters));
8080
///<summary>POST on /_async_search <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
8181
///<param name = "body">The search definition using the Query DSL</param>

src/Nest/Descriptors.AsyncSearch.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,29 @@ protected AsyncSearchGetDescriptor(): base()
8383
public AsyncSearchGetDescriptor WaitForCompletionTimeout(Time waitforcompletiontimeout) => Qs("wait_for_completion_timeout", waitforcompletiontimeout);
8484
}
8585

86+
///<summary>Descriptor for Status <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
87+
public partial class AsyncSearchStatusDescriptor : RequestDescriptorBase<AsyncSearchStatusDescriptor, AsyncSearchStatusRequestParameters, IAsyncSearchStatusRequest>, IAsyncSearchStatusRequest
88+
{
89+
internal override ApiUrls ApiUrls => ApiUrlsLookups.AsyncSearchStatus;
90+
protected override HttpMethod HttpMethod => HttpMethod.GET;
91+
protected override bool SupportsBody => false;
92+
///<summary>/_async_search/status/{id}</summary>
93+
///<param name = "id">this parameter is required</param>
94+
public AsyncSearchStatusDescriptor(Id id): base(r => r.Required("id", id))
95+
{
96+
}
97+
98+
///<summary>Used for serialization purposes, making sure we have a parameterless constructor</summary>
99+
[SerializationConstructor]
100+
protected AsyncSearchStatusDescriptor(): base()
101+
{
102+
}
103+
104+
// values part of the url path
105+
Id IAsyncSearchStatusRequest.Id => Self.RouteValues.Get<Id>("id");
106+
// Request parameters
107+
}
108+
86109
///<summary>Descriptor for Submit <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
87110
public partial class AsyncSearchSubmitDescriptor<TInferDocument> : RequestDescriptorBase<AsyncSearchSubmitDescriptor<TInferDocument>, AsyncSearchSubmitRequestParameters, IAsyncSearchSubmitRequest<TInferDocument>>, IAsyncSearchSubmitRequest<TInferDocument>
88111
{

src/Nest/ElasticClient.AsyncSearch.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,30 @@ public AsyncSearchGetResponse<TDocument> Get<TDocument>(IAsyncSearchGetRequest r
8989
public Task<AsyncSearchGetResponse<TDocument>> GetAsync<TDocument>(IAsyncSearchGetRequest request, CancellationToken ct = default)
9090
where TDocument : class => DoRequestAsync<IAsyncSearchGetRequest, AsyncSearchGetResponse<TDocument>>(request, request.RequestParameters, ct);
9191
/// <summary>
92+
/// <c>GET</c> request to the <c>async_search.status</c> API, read more about this API online:
93+
/// <para></para>
94+
/// <a href = "https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</a>
95+
/// </summary>
96+
public AsyncSearchStatusResponse Status(Id id, Func<AsyncSearchStatusDescriptor, IAsyncSearchStatusRequest> selector = null) => Status(selector.InvokeOrDefault(new AsyncSearchStatusDescriptor(id: id)));
97+
/// <summary>
98+
/// <c>GET</c> request to the <c>async_search.status</c> API, read more about this API online:
99+
/// <para></para>
100+
/// <a href = "https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</a>
101+
/// </summary>
102+
public Task<AsyncSearchStatusResponse> StatusAsync(Id id, Func<AsyncSearchStatusDescriptor, IAsyncSearchStatusRequest> selector = null, CancellationToken ct = default) => StatusAsync(selector.InvokeOrDefault(new AsyncSearchStatusDescriptor(id: id)), ct);
103+
/// <summary>
104+
/// <c>GET</c> request to the <c>async_search.status</c> API, read more about this API online:
105+
/// <para></para>
106+
/// <a href = "https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</a>
107+
/// </summary>
108+
public AsyncSearchStatusResponse Status(IAsyncSearchStatusRequest request) => DoRequest<IAsyncSearchStatusRequest, AsyncSearchStatusResponse>(request, request.RequestParameters);
109+
/// <summary>
110+
/// <c>GET</c> request to the <c>async_search.status</c> API, read more about this API online:
111+
/// <para></para>
112+
/// <a href = "https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</a>
113+
/// </summary>
114+
public Task<AsyncSearchStatusResponse> StatusAsync(IAsyncSearchStatusRequest request, CancellationToken ct = default) => DoRequestAsync<IAsyncSearchStatusRequest, AsyncSearchStatusResponse>(request, request.RequestParameters, ct);
115+
/// <summary>
92116
/// <c>POST</c> request to the <c>async_search.submit</c> API, read more about this API online:
93117
/// <para></para>
94118
/// <a href = "https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</a>

src/Nest/Requests.AsyncSearch.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,41 @@ public Time WaitForCompletionTimeout
122122
}
123123
}
124124

125+
[InterfaceDataContract]
126+
public partial interface IAsyncSearchStatusRequest : IRequest<AsyncSearchStatusRequestParameters>
127+
{
128+
[IgnoreDataMember]
129+
Id Id
130+
{
131+
get;
132+
}
133+
}
134+
135+
///<summary>Request for Status <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
136+
public partial class AsyncSearchStatusRequest : PlainRequestBase<AsyncSearchStatusRequestParameters>, IAsyncSearchStatusRequest
137+
{
138+
protected IAsyncSearchStatusRequest Self => this;
139+
internal override ApiUrls ApiUrls => ApiUrlsLookups.AsyncSearchStatus;
140+
protected override HttpMethod HttpMethod => HttpMethod.GET;
141+
protected override bool SupportsBody => false;
142+
///<summary>/_async_search/status/{id}</summary>
143+
///<param name = "id">this parameter is required</param>
144+
public AsyncSearchStatusRequest(Id id): base(r => r.Required("id", id))
145+
{
146+
}
147+
148+
///<summary>Used for serialization purposes, making sure we have a parameterless constructor</summary>
149+
[SerializationConstructor]
150+
protected AsyncSearchStatusRequest(): base()
151+
{
152+
}
153+
154+
// values part of the url path
155+
[IgnoreDataMember]
156+
Id IAsyncSearchStatusRequest.Id => Self.RouteValues.Get<Id>("id");
157+
// Request parameters
158+
}
159+
125160
[InterfaceDataContract]
126161
public partial interface IAsyncSearchSubmitRequest : IRequest<AsyncSearchSubmitRequestParameters>
127162
{
@@ -402,4 +437,4 @@ public AsyncSearchSubmitRequest(Indices index): base(index)
402437
{
403438
}
404439
}
405-
}
440+
}

src/Nest/XPack/AsyncSearch/AsyncSearch.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
15
using System.Collections.Generic;
26
using System.Linq;
37
using System.Runtime.Serialization;

src/Nest/XPack/AsyncSearch/AsyncSearchResponseBase.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
15
using System;
26
using System.Runtime.Serialization;
37
using Nest.Utf8Json;

src/Nest/XPack/AsyncSearch/Delete/AsyncSearchDeleteRequest.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
15
namespace Nest
26
{
37
[MapsApi("async_search.delete.json")]

src/Nest/XPack/AsyncSearch/Delete/AsyncSearchDeleteResponse.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
15
namespace Nest
26
{
37
public class AsyncSearchDeleteResponse : AcknowledgedResponseBase { }

src/Nest/XPack/AsyncSearch/Get/AsyncSearchGetRequest.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
15
namespace Nest
26
{
37
[MapsApi("async_search.get.json")]

src/Nest/XPack/AsyncSearch/Get/AsyncSearchGetResponse.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
15
namespace Nest
26
{
37
public class AsyncSearchGetResponse<TDocument> : AsyncSearchResponseBase<TDocument> where TDocument : class { }
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
namespace Nest
6+
{
7+
[MapsApi("async_search.status.json")]
8+
[ReadAs(typeof(AsyncSearchStatusRequest))]
9+
public partial interface IAsyncSearchStatusRequest { }
10+
11+
/// <inheritdoc cref="IAsyncSearchStatusRequest"/>
12+
public partial class AsyncSearchStatusRequest
13+
{
14+
}
15+
16+
/// <inheritdoc cref="IAsyncSearchStatusRequest"/>
17+
public partial class AsyncSearchStatusDescriptor
18+
{
19+
}
20+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using System;
6+
using System.Runtime.Serialization;
7+
8+
namespace Nest
9+
{
10+
public class AsyncSearchStatusResponse : ResponseBase
11+
{
12+
[DataMember(Name = "_shards")]
13+
public ShardStatistics Shards { get; internal set; }
14+
15+
[DataMember(Name = "completion_status")]
16+
public int? CompletionStatus { get; internal set; }
17+
18+
[DataMember(Name = "id")]
19+
public string Id { get; internal set; }
20+
21+
[DataMember(Name = "is_partial")]
22+
public bool IsPartial { get; internal set; }
23+
24+
[DataMember(Name = "start_time_in_millis")]
25+
public long StartTimeInMilliseconds { get; internal set; }
26+
27+
[IgnoreDataMember]
28+
public DateTimeOffset StartTime => DateTimeUtil.UnixEpoch.AddMilliseconds(StartTimeInMilliseconds);
29+
30+
[DataMember(Name = "is_running")]
31+
public bool IsRunning { get; internal set; }
32+
33+
[DataMember(Name = "expiration_time_in_millis")]
34+
public long ExpirationTimeInMilliseconds { get; internal set; }
35+
36+
[IgnoreDataMember]
37+
public DateTimeOffset ExpirationTime => DateTimeUtil.UnixEpoch.AddMilliseconds(ExpirationTimeInMilliseconds);
38+
}
39+
}

src/Nest/XPack/AsyncSearch/Submit/AsyncSearchSubmitRequest.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
15
using System;
26
using System.Collections.Generic;
37
using System.Runtime.Serialization;

src/Nest/XPack/AsyncSearch/Submit/AsyncSearchSubmitResponse.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
15
namespace Nest
26
{
37
public class AsyncSearchSubmitResponse<TDocument> : AsyncSearchResponseBase<TDocument>

src/Nest/_Generated/ApiUrlsLookup.generated.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ internal static class ApiUrlsLookups
2020
{
2121
internal static ApiUrls AsyncSearchDelete = new ApiUrls(new[]{"_async_search/{id}"});
2222
internal static ApiUrls AsyncSearchGet = new ApiUrls(new[]{"_async_search/{id}"});
23+
internal static ApiUrls AsyncSearchStatus = new ApiUrls(new[]{"_async_search/status/{id}"});
2324
internal static ApiUrls AsyncSearchSubmit = new ApiUrls(new[]{"_async_search", "{index}/_async_search"});
2425
internal static ApiUrls NoNamespaceBulk = new ApiUrls(new[]{"_bulk", "{index}/_bulk", "{index}/{type}/_bulk"});
2526
internal static ApiUrls CatAliases = new ApiUrls(new[]{"_cat/aliases", "_cat/aliases/{name}"});

tests/Tests/XPack/AsyncSearch/AsyncSearchApiTests.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace Tests.XPack.AsyncSearch
1616
public class AsyncSearchApiTests : CoordinatedIntegrationTestBase<ReadOnlyCluster>
1717
{
1818
private const string SubmitStep = nameof(SubmitStep);
19+
private const string StatusStep = nameof(StatusStep);
1920
private const string GetStep = nameof(GetStep);
2021
private const string DeleteStep = nameof(DeleteStep);
2122

@@ -70,6 +71,17 @@ public AsyncSearchApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(
7071
onResponse: (r, values) => values.ExtendedValue("id", r.Id)
7172
)
7273
},
74+
{StatusStep, ">=7.11.0", u =>
75+
u.Calls<AsyncSearchStatusDescriptor, AsyncSearchStatusRequest, IAsyncSearchStatusRequest, AsyncSearchStatusResponse>(
76+
v => new AsyncSearchStatusRequest(v),
77+
(v, d) => d,
78+
(v, c, f) => c.AsyncSearch.Status(v, f),
79+
(v, c, f) => c.AsyncSearch.StatusAsync(v, f),
80+
(v, c, r) => c.AsyncSearch.Status(r),
81+
(v, c, r) => c.AsyncSearch.StatusAsync(r),
82+
uniqueValueSelector: values => values.ExtendedValue<string>("id")
83+
)
84+
},
7385
{GetStep, u =>
7486
u.Calls<AsyncSearchGetDescriptor, AsyncSearchGetRequest, IAsyncSearchGetRequest, AsyncSearchGetResponse<Project>>(
7587
v => new AsyncSearchGetRequest(v),
@@ -94,26 +106,41 @@ public AsyncSearchApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(
94106
},
95107
}) { }
96108

97-
[I] public async Task AsyncSearchSubmitResponse() => await Assert<AsyncSearchSubmitResponse<Project>>(SubmitStep, (v, r) =>
109+
[I] public async Task AsyncSearchSubmitResponse() => await Assert<AsyncSearchSubmitResponse<Project>>(SubmitStep, r =>
98110
{
99111
r.ShouldBeValid();
100112
r.Response.Should().NotBeNull();
101113
r.Response.Took.Should().BeGreaterOrEqualTo(0);
102114
});
115+
116+
[I] public async Task AsyncSearchStatusResponse() => await Assert<AsyncSearchStatusResponse>(StatusStep, r =>
117+
{
118+
r.ShouldBeValid();
119+
r.StartTime.Should().BeOnOrBefore(DateTimeOffset.Now);
120+
r.ExpirationTime.Should().BeOnOrAfter(DateTimeOffset.Now);
121+
122+
if (r.IsRunning)
123+
r.CompletionStatus.HasValue.Should().BeFalse();
124+
else
125+
r.CompletionStatus?.Should().Be(200);
126+
127+
r.Shards.Total.Should().BeGreaterOrEqualTo(1);
128+
});
103129

104-
[I] public async Task AsyncSearchGetResponse() => await Assert<AsyncSearchGetResponse<Project>>(GetStep, (v, r) =>
130+
[I] public async Task AsyncSearchGetResponse() => await Assert<AsyncSearchGetResponse<Project>>(GetStep, r =>
105131
{
106132
r.ShouldBeValid();
107133
r.Id.Should().NotBeNullOrEmpty();
108134
r.StartTime.Should().BeOnOrBefore(DateTimeOffset.Now);
135+
r.ExpirationTime.Should().BeOnOrAfter(DateTimeOffset.Now);
109136
r.Response.Should().NotBeNull();
110137
r.Response.Took.Should().BeGreaterOrEqualTo(0);
111138
r.Response.Hits.Should().HaveCount(10);
112139
var terms = r.Response.Aggregations.Terms("states");
113140
terms.Should().NotBeNull();
114141
});
115142

116-
[I] public async Task AsyncSearchDeleteResponse() => await Assert<AsyncSearchDeleteResponse>(DeleteStep, (v, r) =>
143+
[I] public async Task AsyncSearchDeleteResponse() => await Assert<AsyncSearchDeleteResponse>(DeleteStep, r =>
117144
{
118145
r.ShouldBeValid();
119146
r.Acknowledged.Should().BeTrue();
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System.Threading.Tasks;
2+
using Elastic.Elasticsearch.Xunit.XunitPlumbing;
3+
using Nest;
4+
using Tests.Framework.EndpointTests;
5+
using static Tests.Framework.EndpointTests.UrlTester;
6+
7+
namespace Tests.XPack.AsyncSearch.Status
8+
{
9+
public class AsyncSearchStatusUrlTests : UrlTestsBase
10+
{
11+
[U] public override async Task Urls() => await GET("/_async_search/status/search_id")
12+
.Fluent(c => c.AsyncSearch.Status("search_id", f => f))
13+
.Request(c => c.AsyncSearch.Status(new AsyncSearchStatusRequest("search_id")))
14+
.FluentAsync(c => c.AsyncSearch.StatusAsync("search_id", f => f))
15+
.RequestAsync(c => c.AsyncSearch.StatusAsync(new AsyncSearchStatusRequest("search_id")));
16+
}
17+
}

0 commit comments

Comments
 (0)