Skip to content

Commit 190d4e5

Browse files
stevejgordongithub-actions[bot]
authored andcommitted
Allow formatting of sort values (#5610)
1 parent 3bff8f1 commit 190d4e5

File tree

2 files changed

+88
-18
lines changed

2 files changed

+88
-18
lines changed

src/Nest/Search/Search/Sort/SortBase.cs

+13
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ namespace Nest
1212
[JsonFormatter(typeof(SortFormatter))]
1313
public interface ISort
1414
{
15+
/// <summary>
16+
/// A format to apply to the sort value.
17+
/// </summary>
18+
[DataMember(Name ="format")]
19+
string Format { get; set; }
20+
1521
/// <summary>
1622
/// Specifies how documents which are missing the sort field should
1723
/// be treated.
@@ -53,6 +59,9 @@ public interface ISort
5359

5460
public abstract class SortBase : ISort
5561
{
62+
/// <inheritdoc />
63+
public string Format { get; set; }
64+
5665
/// <inheritdoc />
5766
public object Missing { get; set; }
5867

@@ -87,6 +96,7 @@ public abstract class SortDescriptorBase<TDescriptor, TInterface, T> : Descripto
8796
/// </summary>
8897
protected abstract Field SortKey { get; }
8998

99+
string ISort.Format { get; set; }
90100
object ISort.Missing { get; set; }
91101
SortMode? ISort.Mode { get; set; }
92102
NumericType? ISort.NumericType { get; set; }
@@ -103,6 +113,9 @@ public abstract class SortDescriptorBase<TDescriptor, TInterface, T> : Descripto
103113
/// Sorts by descending sort order
104114
/// </summary>
105115
public virtual TDescriptor Descending() => Assign(SortOrder.Descending, (a, v) => a.Order = v);
116+
117+
/// <inheritdoc cref="ISort.Format" />
118+
public virtual TDescriptor Format(string format) => Assign(format, (a, v) => a.Format = v);
106119

107120
/// <inheritdoc cref="ISort.Order" />
108121
public virtual TDescriptor Order(SortOrder? order) => Assign(order, (a, v) => a.Order = v);

tests/Tests/Search/Search/SearchApiTests.cs

+75-18
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ protected override void ExpectResponse(ISearchResponse<Project> response)
111111
var startDates = response.Aggregations.Terms("startDates");
112112
startDates.Should().NotBeNull();
113113

114-
foreach (var document in response.Documents) document.ShouldAdhereToSourceSerializerWhenSet();
114+
foreach (var document in response.Documents)
115+
document.ShouldAdhereToSourceSerializerWhenSet();
115116
}
116117
}
117118

@@ -540,20 +541,20 @@ protected override void ExpectResponse(ListTasksResponse response)
540541
{
541542
response.ShouldBeValid();
542543
foreach (var node in response.Nodes)
543-
foreach (var task in node.Value.Tasks)
544-
{
545-
task.Value.Headers.Should().NotBeNull();
546-
if (task.Value.Headers.TryGetValue(RequestData.OpaqueIdHeader, out var opaqueIdValue))
547-
opaqueIdValue.Should()
548-
.Be(CallIsolatedValue,
549-
$"OpaqueId header {opaqueIdValue} did not match {CallIsolatedValue}");
550-
// TODO: Determine if this is a valid assertion i.e. should all tasks returned have an OpaqueId header?
551-
// else
552-
// {
553-
// Assert.True(false,
554-
// $"No OpaqueId header for task {task.Key} and OpaqueId value {this.CallIsolatedValue}");
555-
// }
556-
}
544+
foreach (var task in node.Value.Tasks)
545+
{
546+
task.Value.Headers.Should().NotBeNull();
547+
if (task.Value.Headers.TryGetValue(RequestData.OpaqueIdHeader, out var opaqueIdValue))
548+
opaqueIdValue.Should()
549+
.Be(CallIsolatedValue,
550+
$"OpaqueId header {opaqueIdValue} did not match {CallIsolatedValue}");
551+
// TODO: Determine if this is a valid assertion i.e. should all tasks returned have an OpaqueId header?
552+
// else
553+
// {
554+
// Assert.True(false,
555+
// $"No OpaqueId header for task {task.Key} and OpaqueId value {this.CallIsolatedValue}");
556+
// }
557+
}
557558
}
558559
}
559560

@@ -610,7 +611,7 @@ public class SearchWithPointInTimeApiTests
610611
: ApiTestBase<ReadOnlyCluster, ISearchResponse<Project>, ISearchRequest, SearchDescriptor<Project>, SearchRequest<Project>>
611612
{
612613
public SearchWithPointInTimeApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
613-
614+
614615
protected override object ExpectJson => new
615616
{
616617
size = 1,
@@ -624,7 +625,7 @@ public SearchWithPointInTimeApiTests(ReadOnlyCluster cluster, EndpointUsage usag
624625
keep_alive = "1m"
625626
}
626627
};
627-
628+
628629
protected override Func<SearchDescriptor<Project>, ISearchRequest> Fluent => s => s
629630
.Size(1)
630631
.Query(q => q.MatchAll())
@@ -675,7 +676,7 @@ public SearchApiRuntimeFieldsTests(ReadOnlyCluster cluster, EndpointUsage usage)
675676
},
676677
"search_runtime_field"
677678
},
678-
runtime_mappings = new
679+
runtime_mappings = new
679680
{
680681
search_runtime_field = new
681682
{
@@ -740,4 +741,60 @@ protected override void ExpectResponse(ISearchResponse<Project> response)
740741
}
741742
}
742743
}
744+
745+
[SkipVersion("<7.13.0", "Format for sort values added in Elasticsearch 7.13.0")]
746+
public class SearchApiSortFormatTests : SearchApiTests
747+
{
748+
public SearchApiSortFormatTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
749+
750+
protected override object ExpectJson => new
751+
{
752+
size = 5,
753+
query = new
754+
{
755+
match_all = new { }
756+
},
757+
sort = new[]
758+
{
759+
new
760+
{
761+
startedOn = new
762+
{
763+
format = "strict_date_optional_time_nanos",
764+
mode = "avg",
765+
order = "asc"
766+
}
767+
}
768+
}
769+
};
770+
771+
protected override Func<SearchDescriptor<Project>, ISearchRequest> Fluent => s => s
772+
.Size(5)
773+
.Query(q => q
774+
.MatchAll()
775+
)
776+
.Sort(srt => srt.Field(f => f
777+
.Field(fld => fld.StartedOn)
778+
.Order(SortOrder.Ascending)
779+
.Format("strict_date_optional_time_nanos")
780+
.Mode(SortMode.Average)));
781+
782+
protected override SearchRequest<Project> Initializer => new()
783+
{
784+
Size = 5,
785+
Query = new QueryContainer(new MatchAllQuery()),
786+
Sort = new List<ISort>
787+
{
788+
new FieldSort
789+
{
790+
Field = Infer.Field<Project>(f => f.StartedOn),
791+
Format = "strict_date_optional_time_nanos",
792+
Mode = SortMode.Average,
793+
Order = SortOrder.Ascending
794+
}
795+
}
796+
};
797+
798+
protected override void ExpectResponse(ISearchResponse<Project> response) => response.Hits.Count.Should().BeGreaterThan(0);
799+
}
743800
}

0 commit comments

Comments
 (0)