diff --git a/src/Nest/Search/Search/Sort/SortBase.cs b/src/Nest/Search/Search/Sort/SortBase.cs
index d76c9a1fce1..ed9fc6ab43a 100644
--- a/src/Nest/Search/Search/Sort/SortBase.cs
+++ b/src/Nest/Search/Search/Sort/SortBase.cs
@@ -12,6 +12,12 @@ namespace Nest
[JsonFormatter(typeof(SortFormatter))]
public interface ISort
{
+ ///
+ /// A format to apply to the sort value.
+ ///
+ [DataMember(Name ="format")]
+ string Format { get; set; }
+
///
/// Specifies how documents which are missing the sort field should
/// be treated.
@@ -53,6 +59,9 @@ public interface ISort
public abstract class SortBase : ISort
{
+ ///
+ public string Format { get; set; }
+
///
public object Missing { get; set; }
@@ -87,6 +96,7 @@ public abstract class SortDescriptorBase : Descripto
///
protected abstract Field SortKey { get; }
+ string ISort.Format { get; set; }
object ISort.Missing { get; set; }
SortMode? ISort.Mode { get; set; }
NumericType? ISort.NumericType { get; set; }
@@ -103,6 +113,9 @@ public abstract class SortDescriptorBase : Descripto
/// Sorts by descending sort order
///
public virtual TDescriptor Descending() => Assign(SortOrder.Descending, (a, v) => a.Order = v);
+
+ ///
+ public virtual TDescriptor Format(string format) => Assign(format, (a, v) => a.Format = v);
///
public virtual TDescriptor Order(SortOrder? order) => Assign(order, (a, v) => a.Order = v);
diff --git a/tests/Tests/Search/Search/SearchApiTests.cs b/tests/Tests/Search/Search/SearchApiTests.cs
index 482a8e7b822..52913cb1376 100644
--- a/tests/Tests/Search/Search/SearchApiTests.cs
+++ b/tests/Tests/Search/Search/SearchApiTests.cs
@@ -111,7 +111,8 @@ protected override void ExpectResponse(ISearchResponse response)
var startDates = response.Aggregations.Terms("startDates");
startDates.Should().NotBeNull();
- foreach (var document in response.Documents) document.ShouldAdhereToSourceSerializerWhenSet();
+ foreach (var document in response.Documents)
+ document.ShouldAdhereToSourceSerializerWhenSet();
}
}
@@ -540,20 +541,20 @@ protected override void ExpectResponse(ListTasksResponse response)
{
response.ShouldBeValid();
foreach (var node in response.Nodes)
- foreach (var task in node.Value.Tasks)
- {
- task.Value.Headers.Should().NotBeNull();
- if (task.Value.Headers.TryGetValue(RequestData.OpaqueIdHeader, out var opaqueIdValue))
- opaqueIdValue.Should()
- .Be(CallIsolatedValue,
- $"OpaqueId header {opaqueIdValue} did not match {CallIsolatedValue}");
- // TODO: Determine if this is a valid assertion i.e. should all tasks returned have an OpaqueId header?
-// else
-// {
-// Assert.True(false,
-// $"No OpaqueId header for task {task.Key} and OpaqueId value {this.CallIsolatedValue}");
-// }
- }
+ foreach (var task in node.Value.Tasks)
+ {
+ task.Value.Headers.Should().NotBeNull();
+ if (task.Value.Headers.TryGetValue(RequestData.OpaqueIdHeader, out var opaqueIdValue))
+ opaqueIdValue.Should()
+ .Be(CallIsolatedValue,
+ $"OpaqueId header {opaqueIdValue} did not match {CallIsolatedValue}");
+ // TODO: Determine if this is a valid assertion i.e. should all tasks returned have an OpaqueId header?
+ // else
+ // {
+ // Assert.True(false,
+ // $"No OpaqueId header for task {task.Key} and OpaqueId value {this.CallIsolatedValue}");
+ // }
+ }
}
}
@@ -610,7 +611,7 @@ public class SearchWithPointInTimeApiTests
: ApiTestBase, ISearchRequest, SearchDescriptor, SearchRequest>
{
public SearchWithPointInTimeApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
-
+
protected override object ExpectJson => new
{
size = 1,
@@ -624,7 +625,7 @@ public SearchWithPointInTimeApiTests(ReadOnlyCluster cluster, EndpointUsage usag
keep_alive = "1m"
}
};
-
+
protected override Func, ISearchRequest> Fluent => s => s
.Size(1)
.Query(q => q.MatchAll())
@@ -675,7 +676,7 @@ public SearchApiRuntimeFieldsTests(ReadOnlyCluster cluster, EndpointUsage usage)
},
"search_runtime_field"
},
- runtime_mappings = new
+ runtime_mappings = new
{
search_runtime_field = new
{
@@ -740,4 +741,60 @@ protected override void ExpectResponse(ISearchResponse response)
}
}
}
+
+ [SkipVersion("<7.13.0", "Format for sort values added in Elasticsearch 7.13.0")]
+ public class SearchApiSortFormatTests : SearchApiTests
+ {
+ public SearchApiSortFormatTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
+
+ protected override object ExpectJson => new
+ {
+ size = 5,
+ query = new
+ {
+ match_all = new { }
+ },
+ sort = new[]
+ {
+ new
+ {
+ startedOn = new
+ {
+ format = "strict_date_optional_time_nanos",
+ mode = "avg",
+ order = "asc"
+ }
+ }
+ }
+ };
+
+ protected override Func, ISearchRequest> Fluent => s => s
+ .Size(5)
+ .Query(q => q
+ .MatchAll()
+ )
+ .Sort(srt => srt.Field(f => f
+ .Field(fld => fld.StartedOn)
+ .Order(SortOrder.Ascending)
+ .Format("strict_date_optional_time_nanos")
+ .Mode(SortMode.Average)));
+
+ protected override SearchRequest Initializer => new()
+ {
+ Size = 5,
+ Query = new QueryContainer(new MatchAllQuery()),
+ Sort = new List
+ {
+ new FieldSort
+ {
+ Field = Infer.Field(f => f.StartedOn),
+ Format = "strict_date_optional_time_nanos",
+ Mode = SortMode.Average,
+ Order = SortOrder.Ascending
+ }
+ }
+ };
+
+ protected override void ExpectResponse(ISearchResponse response) => response.Hits.Count.Should().BeGreaterThan(0);
+ }
}