diff --git a/src/Nest/Search/Search/Sort/FieldSort.cs b/src/Nest/Search/Search/Sort/FieldSort.cs index 03dfcb2846f..e4f15d69470 100644 --- a/src/Nest/Search/Search/Sort/FieldSort.cs +++ b/src/Nest/Search/Search/Sort/FieldSort.cs @@ -23,7 +23,14 @@ public interface IFieldSort : ISort public class FieldSort : SortBase, IFieldSort { + private const string ShardDoc = "_shard_doc"; + public static readonly IList ByDocumentOrder = new ReadOnlyCollection(new List { new FieldSort { Field = "_doc" } }); + public static readonly IList ByShardDocumentOrder = new ReadOnlyCollection(new List { new FieldSort { Field = ShardDoc } }); + + public static readonly FieldSort ShardDocumentOrderAscending = new() { Field = ShardDoc, Order = SortOrder.Ascending }; + public static readonly FieldSort ShardDocumentOrderDescending = new() { Field = ShardDoc, Order = SortOrder.Descending }; + public Field Field { get; set; } public bool? IgnoreUnmappedFields { get; set; } public FieldType? UnmappedType { get; set; } diff --git a/src/Nest/Search/Search/Sort/SortSpecialField.cs b/src/Nest/Search/Search/Sort/SortSpecialField.cs index 8007b998cb9..a1af3b32bf5 100644 --- a/src/Nest/Search/Search/Sort/SortSpecialField.cs +++ b/src/Nest/Search/Search/Sort/SortSpecialField.cs @@ -15,6 +15,9 @@ public enum SortSpecialField Score, [EnumMember(Value = "_doc")] - DocumentIndexOrder + DocumentIndexOrder, + + [EnumMember(Value = "_shard_doc")] + ShardDocumentOrder } } diff --git a/tests/Tests/Search/PointInTime/PointInTimeApiTests.cs b/tests/Tests/Search/PointInTime/PointInTimeApiTests.cs index 676c7a4c5f0..8e8d79d7a2d 100644 --- a/tests/Tests/Search/PointInTime/PointInTimeApiTests.cs +++ b/tests/Tests/Search/PointInTime/PointInTimeApiTests.cs @@ -2,7 +2,7 @@ // 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.Linq; using System.Threading.Tasks; using Elastic.Elasticsearch.Xunit.XunitPlumbing; @@ -21,6 +21,7 @@ public class PointInTimeApiTests : CoordinatedIntegrationTestBase values.ExtendedValue("pitId") ) }, + { + SearchPointInTimeWithSortStep, ">=7.12.0", u => + u.Calls, SearchRequest, ISearchRequest, ISearchResponse>( + v => new SearchRequest + { + Size = 1, + Query = new QueryContainer(new MatchAllQuery()), + PointInTime = new Nest.PointInTime(v, "1m"), + Sort =new List + { + FieldSort.ShardDocumentOrderDescending + } + }, + (v, d) => d + .Size(1) + .Query(q => q.MatchAll()) + .PointInTime(v, p => p.KeepAlive("1m")) + .Sort(s => s.Descending(SortSpecialField.ShardDocumentOrder)), + (v, c, f) => c.Search(f), + (v, c, f) => c.SearchAsync(f), + (v, c, r) => c.Search(r), + (v, c, r) => c.SearchAsync(r), + uniqueValueSelector: values => values.ExtendedValue("pitId") + ) + }, { ClosePointInTimeStep, u => u.Calls( @@ -74,13 +100,13 @@ public PointInTimeApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base( } }) { } - [I] public async Task OpenPointInTimeResponse() => await Assert(OpenPointInTimeStep, (v, r) => + [I] public async Task OpenPointInTimeResponse() => await Assert(OpenPointInTimeStep, r => { r.ShouldBeValid(); r.Id.Should().NotBeNullOrEmpty(); }); - [I] public async Task SearchPointInTimeResponse() => await Assert>(SearchPointInTimeStep, (v, r) => + [I] public async Task SearchPointInTimeResponse() => await Assert>(SearchPointInTimeStep, r => { r.ShouldBeValid(); r.PointInTimeId.Should().NotBeNullOrEmpty(); @@ -93,7 +119,15 @@ [I] public async Task SearchPointInTimeResponse() => await Assert await Assert(ClosePointInTimeStep, (v, r) => + [I] public async Task SearchPointInTimeWithSortResponse() => await Assert>(SearchPointInTimeWithSortStep, r => + { + r.ShouldBeValid(); + r.PointInTimeId.Should().NotBeNullOrEmpty(); + r.Total.Should().BeGreaterThan(0); + r.Hits.Count.Should().BeGreaterThan(0); + }); + + [I] public async Task ClosePointInTimeResponse() => await Assert(ClosePointInTimeStep, r => { r.ShouldBeValid(); r.Succeeded.Should().BeTrue(); diff --git a/tests/Tests/Search/Request/SortUsageTests.cs b/tests/Tests/Search/Request/SortUsageTests.cs index fc1f8979f9e..f6c5f44a641 100644 --- a/tests/Tests/Search/Request/SortUsageTests.cs +++ b/tests/Tests/Search/Request/SortUsageTests.cs @@ -6,9 +6,11 @@ using System.Collections.Generic; using System.Linq; using Elastic.Elasticsearch.Xunit.XunitPlumbing; +using Elasticsearch.Net; using Nest; using Tests.Core.ManagedElasticsearch.Clusters; using Tests.Domain; +using Tests.Framework.EndpointTests; using Tests.Framework.EndpointTests.TestState; using static Nest.Infer; @@ -400,4 +402,56 @@ public NumericTypeUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : bas } }; } + + //hide + [SkipVersion("<7.12.0", "_shard_doc added in 7.12.0")] + public class ShardDocUsageTests : ApiIntegrationTestBase, ISearchRequest, SearchDescriptor, SearchRequest> + { + public ShardDocUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { } + + private string _pit = string.Empty; + + protected override void IntegrationSetup(IElasticClient client, CallUniqueValues values) + { + var response = client.OpenPointInTime(Nest.Indices.Index(), f => f.KeepAlive("1m")); + _pit = response.Id; + } + + protected override object ExpectJson => + new + { + pit = new + { + id = "" + }, + sort = new object[] + { + new { _shard_doc = new { order = "asc" } } + } + }; + + protected override HttpMethod HttpMethod => HttpMethod.POST; + protected override string UrlPath => "/project/_search"; + protected override Func, ISearchRequest> Fluent => s => s + .PointInTime(_pit) + .Sort(ss => ss.Ascending(SortSpecialField.ShardDocumentOrder)); + + protected override bool ExpectIsValid => true; + protected override int ExpectStatusCode => 200; + + protected override SearchRequest Initializer => new() + { + PointInTime = new Nest.PointInTime(_pit), + Sort = new List + { + FieldSort.ShardDocumentOrderAscending + } + }; + + protected override LazyResponses ClientUsage() => Calls( + (client, f) => client.Search(f), + (client, f) => client.SearchAsync(f), + (client, r) => client.Search(r), + (client, r) => client.SearchAsync(r)); + } }