Skip to content

Commit ddefe5b

Browse files
stevejgordongithub-actions[bot]
authored andcommitted
Allow formatting of sort values (#5610)
1 parent 5295324 commit ddefe5b

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

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

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

0 commit comments

Comments
 (0)