Skip to content

Commit 0716ff7

Browse files
stevejgordongithub-actions[bot]
authored andcommitted
Implement AsyncSearch Status API (#5239)
1 parent 981b73c commit 0716ff7

18 files changed

+218
-9
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public TimeSpan WaitForCompletionTimeout
5959
}
6060

6161
///<summary>Request options for Status <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
62-
public class StatusRequestParameters : RequestParameters<StatusRequestParameters>
62+
public class AsyncSearchStatusRequestParameters : RequestParameters<AsyncSearchStatusRequestParameters>
6363
{
6464
public override HttpMethod DefaultHttpMethod => HttpMethod.GET;
6565
public override bool SupportsBody => false;

src/Elasticsearch.Net/ElasticLowLevelClient.AsyncSearch.cs

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

src/Nest/Descriptors.AsyncSearch.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,27 @@ protected AsyncSearchGetDescriptor(): base()
7878
public AsyncSearchGetDescriptor WaitForCompletionTimeout(Time waitforcompletiontimeout) => Qs("wait_for_completion_timeout", waitforcompletiontimeout);
7979
}
8080

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

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: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,39 @@ public Time WaitForCompletionTimeout
117117
}
118118
}
119119

120+
[InterfaceDataContract]
121+
public partial interface IAsyncSearchStatusRequest : IRequest<AsyncSearchStatusRequestParameters>
122+
{
123+
[IgnoreDataMember]
124+
Id Id
125+
{
126+
get;
127+
}
128+
}
129+
130+
///<summary>Request for Status <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
131+
public partial class AsyncSearchStatusRequest : PlainRequestBase<AsyncSearchStatusRequestParameters>, IAsyncSearchStatusRequest
132+
{
133+
protected IAsyncSearchStatusRequest Self => this;
134+
internal override ApiUrls ApiUrls => ApiUrlsLookups.AsyncSearchStatus;
135+
///<summary>/_async_search/status/{id}</summary>
136+
///<param name = "id">this parameter is required</param>
137+
public AsyncSearchStatusRequest(Id id): base(r => r.Required("id", id))
138+
{
139+
}
140+
141+
///<summary>Used for serialization purposes, making sure we have a parameterless constructor</summary>
142+
[SerializationConstructor]
143+
protected AsyncSearchStatusRequest(): base()
144+
{
145+
}
146+
147+
// values part of the url path
148+
[IgnoreDataMember]
149+
Id IAsyncSearchStatusRequest.Id => Self.RouteValues.Get<Id>("id");
150+
// Request parameters
151+
}
152+
120153
[InterfaceDataContract]
121154
public partial interface IAsyncSearchSubmitRequest : IRequest<AsyncSearchSubmitRequestParameters>
122155
{

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 Elasticsearch.Net.Utf8Json;

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Collections.Generic;
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
24

35
namespace Nest
46
{

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: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Collections.Generic;
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
24

35
namespace Nest
46
{

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"});
2526
internal static ApiUrls CatAliases = new ApiUrls(new[]{"_cat/aliases", "_cat/aliases/{name}"});

tests/Tests/XPack/AsyncSearch/AsyncSearchApiTests.cs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections.Concurrent;
32
using System.Collections.Generic;
43
using System.Threading.Tasks;
54
using Elastic.Elasticsearch.Xunit.XunitPlumbing;
@@ -17,6 +16,7 @@ namespace Tests.XPack.AsyncSearch
1716
public class AsyncSearchApiTests : CoordinatedIntegrationTestBase<ReadOnlyCluster>
1817
{
1918
private const string SubmitStep = nameof(SubmitStep);
19+
private const string StatusStep = nameof(StatusStep);
2020
private const string GetStep = nameof(GetStep);
2121
private const string DeleteStep = nameof(DeleteStep);
2222

@@ -71,6 +71,17 @@ public AsyncSearchApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(
7171
onResponse: (r, values) => values.ExtendedValue("id", r.Id)
7272
)
7373
},
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+
},
7485
{GetStep, u =>
7586
u.Calls<AsyncSearchGetDescriptor, AsyncSearchGetRequest, IAsyncSearchGetRequest, AsyncSearchGetResponse<Project>>(
7687
v => new AsyncSearchGetRequest(v),
@@ -95,26 +106,41 @@ public AsyncSearchApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(
95106
},
96107
}) { }
97108

98-
[I] public async Task AsyncSearchSubmitResponse() => await Assert<AsyncSearchSubmitResponse<Project>>(SubmitStep, (v, r) =>
109+
[I] public async Task AsyncSearchSubmitResponse() => await Assert<AsyncSearchSubmitResponse<Project>>(SubmitStep, r =>
99110
{
100111
r.ShouldBeValid();
101112
r.Response.Should().NotBeNull();
102113
r.Response.Took.Should().BeGreaterOrEqualTo(0);
103114
});
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+
});
104129

105-
[I] public async Task AsyncSearchGetResponse() => await Assert<AsyncSearchGetResponse<Project>>(GetStep, (v, r) =>
130+
[I] public async Task AsyncSearchGetResponse() => await Assert<AsyncSearchGetResponse<Project>>(GetStep, r =>
106131
{
107132
r.ShouldBeValid();
108133
r.Id.Should().NotBeNullOrEmpty();
109134
r.StartTime.Should().BeOnOrBefore(DateTimeOffset.Now);
135+
r.ExpirationTime.Should().BeOnOrAfter(DateTimeOffset.Now);
110136
r.Response.Should().NotBeNull();
111137
r.Response.Took.Should().BeGreaterOrEqualTo(0);
112138
r.Response.Hits.Should().HaveCount(10);
113139
var terms = r.Response.Aggregations.Terms("states");
114140
terms.Should().NotBeNull();
115141
});
116142

117-
[I] public async Task AsyncSearchDeleteResponse() => await Assert<AsyncSearchDeleteResponse>(DeleteStep, (v, r) =>
143+
[I] public async Task AsyncSearchDeleteResponse() => await Assert<AsyncSearchDeleteResponse>(DeleteStep, r =>
118144
{
119145
r.ShouldBeValid();
120146
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)