Skip to content

Implement AsyncSearch Status API #5239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public TimeSpan WaitForCompletionTimeout
}

///<summary>Request options for Status <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
public class StatusRequestParameters : RequestParameters<StatusRequestParameters>
public class AsyncSearchStatusRequestParameters : RequestParameters<AsyncSearchStatusRequestParameters>
{
public override HttpMethod DefaultHttpMethod => HttpMethod.GET;
public override bool SupportsBody => false;
Expand Down
4 changes: 2 additions & 2 deletions src/Elasticsearch.Net/ElasticLowLevelClient.AsyncSearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ public Task<TResponse> GetAsync<TResponse>(string id, AsyncSearchGetRequestParam
///<summary>GET on /_async_search/status/{id} <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
///<param name = "id">The async search ID</param>
///<param name = "requestParameters">Request specific configuration such as querystring parameters &amp; request specific connection settings.</param>
public TResponse Status<TResponse>(string id, StatusRequestParameters requestParameters = null)
public TResponse Status<TResponse>(string id, AsyncSearchStatusRequestParameters requestParameters = null)
where TResponse : class, IElasticsearchResponse, new() => DoRequest<TResponse>(GET, Url($"_async_search/status/{id:id}"), null, RequestParams(requestParameters));
///<summary>GET on /_async_search/status/{id} <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
///<param name = "id">The async search ID</param>
///<param name = "requestParameters">Request specific configuration such as querystring parameters &amp; request specific connection settings.</param>
[MapsApi("async_search.status", "id")]
public Task<TResponse> StatusAsync<TResponse>(string id, StatusRequestParameters requestParameters = null, CancellationToken ctx = default)
public Task<TResponse> StatusAsync<TResponse>(string id, AsyncSearchStatusRequestParameters requestParameters = null, CancellationToken ctx = default)
where TResponse : class, IElasticsearchResponse, new() => DoRequestAsync<TResponse>(GET, Url($"_async_search/status/{id:id}"), ctx, null, RequestParams(requestParameters));
///<summary>POST on /_async_search <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
///<param name = "body">The search definition using the Query DSL</param>
Expand Down
21 changes: 21 additions & 0 deletions src/Nest/Descriptors.AsyncSearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,27 @@ protected AsyncSearchGetDescriptor(): base()
public AsyncSearchGetDescriptor WaitForCompletionTimeout(Time waitforcompletiontimeout) => Qs("wait_for_completion_timeout", waitforcompletiontimeout);
}

///<summary>Descriptor for Status <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
public partial class AsyncSearchStatusDescriptor : RequestDescriptorBase<AsyncSearchStatusDescriptor, AsyncSearchStatusRequestParameters, IAsyncSearchStatusRequest>, IAsyncSearchStatusRequest
{
internal override ApiUrls ApiUrls => ApiUrlsLookups.AsyncSearchStatus;
///<summary>/_async_search/status/{id}</summary>
///<param name = "id">this parameter is required</param>
public AsyncSearchStatusDescriptor(Id id): base(r => r.Required("id", id))
{
}

///<summary>Used for serialization purposes, making sure we have a parameterless constructor</summary>
[SerializationConstructor]
protected AsyncSearchStatusDescriptor(): base()
{
}

// values part of the url path
Id IAsyncSearchStatusRequest.Id => Self.RouteValues.Get<Id>("id");
// Request parameters
}

///<summary>Descriptor for Submit <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
public partial class AsyncSearchSubmitDescriptor<TInferDocument> : RequestDescriptorBase<AsyncSearchSubmitDescriptor<TInferDocument>, AsyncSearchSubmitRequestParameters, IAsyncSearchSubmitRequest<TInferDocument>>, IAsyncSearchSubmitRequest<TInferDocument>
{
Expand Down
24 changes: 24 additions & 0 deletions src/Nest/ElasticClient.AsyncSearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,30 @@ public AsyncSearchGetResponse<TDocument> Get<TDocument>(IAsyncSearchGetRequest r
public Task<AsyncSearchGetResponse<TDocument>> GetAsync<TDocument>(IAsyncSearchGetRequest request, CancellationToken ct = default)
where TDocument : class => DoRequestAsync<IAsyncSearchGetRequest, AsyncSearchGetResponse<TDocument>>(request, request.RequestParameters, ct);
/// <summary>
/// <c>GET</c> request to the <c>async_search.status</c> API, read more about this API online:
/// <para></para>
/// <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>
/// </summary>
public AsyncSearchStatusResponse Status(Id id, Func<AsyncSearchStatusDescriptor, IAsyncSearchStatusRequest> selector = null) => Status(selector.InvokeOrDefault(new AsyncSearchStatusDescriptor(id: id)));
/// <summary>
/// <c>GET</c> request to the <c>async_search.status</c> API, read more about this API online:
/// <para></para>
/// <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>
/// </summary>
public Task<AsyncSearchStatusResponse> StatusAsync(Id id, Func<AsyncSearchStatusDescriptor, IAsyncSearchStatusRequest> selector = null, CancellationToken ct = default) => StatusAsync(selector.InvokeOrDefault(new AsyncSearchStatusDescriptor(id: id)), ct);
/// <summary>
/// <c>GET</c> request to the <c>async_search.status</c> API, read more about this API online:
/// <para></para>
/// <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>
/// </summary>
public AsyncSearchStatusResponse Status(IAsyncSearchStatusRequest request) => DoRequest<IAsyncSearchStatusRequest, AsyncSearchStatusResponse>(request, request.RequestParameters);
/// <summary>
/// <c>GET</c> request to the <c>async_search.status</c> API, read more about this API online:
/// <para></para>
/// <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>
/// </summary>
public Task<AsyncSearchStatusResponse> StatusAsync(IAsyncSearchStatusRequest request, CancellationToken ct = default) => DoRequestAsync<IAsyncSearchStatusRequest, AsyncSearchStatusResponse>(request, request.RequestParameters, ct);
/// <summary>
/// <c>POST</c> request to the <c>async_search.submit</c> API, read more about this API online:
/// <para></para>
/// <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>
Expand Down
33 changes: 33 additions & 0 deletions src/Nest/Requests.AsyncSearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,39 @@ public Time WaitForCompletionTimeout
}
}

[InterfaceDataContract]
public partial interface IAsyncSearchStatusRequest : IRequest<AsyncSearchStatusRequestParameters>
{
[IgnoreDataMember]
Id Id
{
get;
}
}

///<summary>Request for Status <para>https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html</para></summary>
public partial class AsyncSearchStatusRequest : PlainRequestBase<AsyncSearchStatusRequestParameters>, IAsyncSearchStatusRequest
{
protected IAsyncSearchStatusRequest Self => this;
internal override ApiUrls ApiUrls => ApiUrlsLookups.AsyncSearchStatus;
///<summary>/_async_search/status/{id}</summary>
///<param name = "id">this parameter is required</param>
public AsyncSearchStatusRequest(Id id): base(r => r.Required("id", id))
{
}

///<summary>Used for serialization purposes, making sure we have a parameterless constructor</summary>
[SerializationConstructor]
protected AsyncSearchStatusRequest(): base()
{
}

// values part of the url path
[IgnoreDataMember]
Id IAsyncSearchStatusRequest.Id => Self.RouteValues.Get<Id>("id");
// Request parameters
}

[InterfaceDataContract]
public partial interface IAsyncSearchSubmitRequest : IRequest<AsyncSearchSubmitRequestParameters>
{
Expand Down
4 changes: 4 additions & 0 deletions src/Nest/XPack/AsyncSearch/AsyncSearch.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
Expand Down
4 changes: 4 additions & 0 deletions src/Nest/XPack/AsyncSearch/AsyncSearchResponseBase.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System;
using System.Runtime.Serialization;
using Elasticsearch.Net.Utf8Json;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Generic;
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

namespace Nest
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

namespace Nest
{
public class AsyncSearchDeleteResponse : AcknowledgedResponseBase { }
Expand Down
4 changes: 3 additions & 1 deletion src/Nest/XPack/AsyncSearch/Get/AsyncSearchGetRequest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Generic;
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

namespace Nest
{
Expand Down
4 changes: 4 additions & 0 deletions src/Nest/XPack/AsyncSearch/Get/AsyncSearchGetResponse.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

namespace Nest
{
public class AsyncSearchGetResponse<TDocument> : AsyncSearchResponseBase<TDocument> where TDocument : class { }
Expand Down
20 changes: 20 additions & 0 deletions src/Nest/XPack/AsyncSearch/Status/AsyncSearchStatusRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

namespace Nest
{
[MapsApi("async_search.status.json")]
[ReadAs(typeof(AsyncSearchStatusRequest))]
public partial interface IAsyncSearchStatusRequest { }

/// <inheritdoc cref="IAsyncSearchStatusRequest"/>
public partial class AsyncSearchStatusRequest
{
}

/// <inheritdoc cref="IAsyncSearchStatusRequest"/>
public partial class AsyncSearchStatusDescriptor
{
}
}
39 changes: 39 additions & 0 deletions src/Nest/XPack/AsyncSearch/Status/AsyncSearchStatusResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System;
using System.Runtime.Serialization;

namespace Nest
{
public class AsyncSearchStatusResponse : ResponseBase
{
[DataMember(Name = "_shards")]
public ShardStatistics Shards { get; internal set; }

[DataMember(Name = "completion_status")]
public int? CompletionStatus { get; internal set; }

[DataMember(Name = "id")]
public string Id { get; internal set; }

[DataMember(Name = "is_partial")]
public bool IsPartial { get; internal set; }

[DataMember(Name = "start_time_in_millis")]
public long StartTimeInMilliseconds { get; internal set; }

[IgnoreDataMember]
public DateTimeOffset StartTime => DateTimeUtil.UnixEpoch.AddMilliseconds(StartTimeInMilliseconds);

[DataMember(Name = "is_running")]
public bool IsRunning { get; internal set; }

[DataMember(Name = "expiration_time_in_millis")]
public long ExpirationTimeInMilliseconds { get; internal set; }

[IgnoreDataMember]
public DateTimeOffset ExpirationTime => DateTimeUtil.UnixEpoch.AddMilliseconds(ExpirationTimeInMilliseconds);
}
}
4 changes: 4 additions & 0 deletions src/Nest/XPack/AsyncSearch/Submit/AsyncSearchSubmitRequest.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

namespace Nest
{
public class AsyncSearchSubmitResponse<TDocument> : AsyncSearchResponseBase<TDocument>
Expand Down
1 change: 1 addition & 0 deletions src/Nest/_Generated/ApiUrlsLookup.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ internal static class ApiUrlsLookups
{
internal static ApiUrls AsyncSearchDelete = new ApiUrls(new[]{"_async_search/{id}"});
internal static ApiUrls AsyncSearchGet = new ApiUrls(new[]{"_async_search/{id}"});
internal static ApiUrls AsyncSearchStatus = new ApiUrls(new[]{"_async_search/status/{id}"});
internal static ApiUrls AsyncSearchSubmit = new ApiUrls(new[]{"_async_search", "{index}/_async_search"});
internal static ApiUrls NoNamespaceBulk = new ApiUrls(new[]{"_bulk", "{index}/_bulk"});
internal static ApiUrls CatAliases = new ApiUrls(new[]{"_cat/aliases", "_cat/aliases/{name}"});
Expand Down
34 changes: 30 additions & 4 deletions tests/Tests/XPack/AsyncSearch/AsyncSearchApiTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using Elastic.Elasticsearch.Xunit.XunitPlumbing;
Expand All @@ -17,6 +16,7 @@ namespace Tests.XPack.AsyncSearch
public class AsyncSearchApiTests : CoordinatedIntegrationTestBase<ReadOnlyCluster>
{
private const string SubmitStep = nameof(SubmitStep);
private const string StatusStep = nameof(StatusStep);
private const string GetStep = nameof(GetStep);
private const string DeleteStep = nameof(DeleteStep);

Expand Down Expand Up @@ -71,6 +71,17 @@ public AsyncSearchApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(
onResponse: (r, values) => values.ExtendedValue("id", r.Id)
)
},
{StatusStep, ">=7.11.0", u =>
u.Calls<AsyncSearchStatusDescriptor, AsyncSearchStatusRequest, IAsyncSearchStatusRequest, AsyncSearchStatusResponse>(
v => new AsyncSearchStatusRequest(v),
(v, d) => d,
(v, c, f) => c.AsyncSearch.Status(v, f),
(v, c, f) => c.AsyncSearch.StatusAsync(v, f),
(v, c, r) => c.AsyncSearch.Status(r),
(v, c, r) => c.AsyncSearch.StatusAsync(r),
uniqueValueSelector: values => values.ExtendedValue<string>("id")
)
},
{GetStep, u =>
u.Calls<AsyncSearchGetDescriptor, AsyncSearchGetRequest, IAsyncSearchGetRequest, AsyncSearchGetResponse<Project>>(
v => new AsyncSearchGetRequest(v),
Expand All @@ -95,26 +106,41 @@ public AsyncSearchApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(
},
}) { }

[I] public async Task AsyncSearchSubmitResponse() => await Assert<AsyncSearchSubmitResponse<Project>>(SubmitStep, (v, r) =>
[I] public async Task AsyncSearchSubmitResponse() => await Assert<AsyncSearchSubmitResponse<Project>>(SubmitStep, r =>
{
r.ShouldBeValid();
r.Response.Should().NotBeNull();
r.Response.Took.Should().BeGreaterOrEqualTo(0);
});

[I] public async Task AsyncSearchStatusResponse() => await Assert<AsyncSearchStatusResponse>(StatusStep, r =>
{
r.ShouldBeValid();
r.StartTime.Should().BeOnOrBefore(DateTimeOffset.Now);
r.ExpirationTime.Should().BeOnOrAfter(DateTimeOffset.Now);

if (r.IsRunning)
r.CompletionStatus.HasValue.Should().BeFalse();
else
r.CompletionStatus?.Should().Be(200);

r.Shards.Total.Should().BeGreaterOrEqualTo(1);
});

[I] public async Task AsyncSearchGetResponse() => await Assert<AsyncSearchGetResponse<Project>>(GetStep, (v, r) =>
[I] public async Task AsyncSearchGetResponse() => await Assert<AsyncSearchGetResponse<Project>>(GetStep, r =>
{
r.ShouldBeValid();
r.Id.Should().NotBeNullOrEmpty();
r.StartTime.Should().BeOnOrBefore(DateTimeOffset.Now);
r.ExpirationTime.Should().BeOnOrAfter(DateTimeOffset.Now);
r.Response.Should().NotBeNull();
r.Response.Took.Should().BeGreaterOrEqualTo(0);
r.Response.Hits.Should().HaveCount(10);
var terms = r.Response.Aggregations.Terms("states");
terms.Should().NotBeNull();
});

[I] public async Task AsyncSearchDeleteResponse() => await Assert<AsyncSearchDeleteResponse>(DeleteStep, (v, r) =>
[I] public async Task AsyncSearchDeleteResponse() => await Assert<AsyncSearchDeleteResponse>(DeleteStep, r =>
{
r.ShouldBeValid();
r.Acknowledged.Should().BeTrue();
Expand Down
17 changes: 17 additions & 0 deletions tests/Tests/XPack/AsyncSearch/Status/AsyncSearchStatusUrlTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Threading.Tasks;
using Elastic.Elasticsearch.Xunit.XunitPlumbing;
using Nest;
using Tests.Framework.EndpointTests;
using static Tests.Framework.EndpointTests.UrlTester;

namespace Tests.XPack.AsyncSearch.Status
{
public class AsyncSearchStatusUrlTests : UrlTestsBase
{
[U] public override async Task Urls() => await GET("/_async_search/status/search_id")
.Fluent(c => c.AsyncSearch.Status("search_id", f => f))
.Request(c => c.AsyncSearch.Status(new AsyncSearchStatusRequest("search_id")))
.FluentAsync(c => c.AsyncSearch.StatusAsync("search_id", f => f))
.RequestAsync(c => c.AsyncSearch.StatusAsync(new AsyncSearchStatusRequest("search_id")));
}
}