From 15632694fc268b41941bae59c18d179e74e24045 Mon Sep 17 00:00:00 2001 From: Steve Gordon Date: Wed, 15 Mar 2023 11:25:48 +0000 Subject: [PATCH] Add support for geo distance sorting. (#7356) * Support geo distance sorting * Add basic bool query test --- .../Types/SortOptions.cs | 12 +- .../Types/Enums/Enums.NoNamespace.g.cs | 42 ++ .../_Generated/Types/GeoDistanceSort.g.cs | 378 ++++++++++++++++++ .../_Generated/Types/SortOptions.g.cs | 6 + .../Framework/EndpointTests/ApiTestBase.cs | 20 + .../QueryDsl/Compound/BoolQueryUsageTests.cs | 39 ++ .../Search/Search/SearchUsageTestBase.cs | 2 - tests/Tests/Search/Search/SortUsageTests.cs | 49 ++- ...ageTests.VerifyDescriptorJson.verified.txt | 25 ++ ...geTests.VerifyInitializerJson.verified.txt | 25 ++ ...ageTests.VerifyDescriptorJson.verified.txt | 32 ++ ...geTests.VerifyInitializerJson.verified.txt | 32 ++ 12 files changed, 643 insertions(+), 19 deletions(-) create mode 100644 src/Elastic.Clients.Elasticsearch/_Generated/Types/GeoDistanceSort.g.cs create mode 100644 tests/Tests/QueryDsl/Compound/BoolQueryUsageTests.cs create mode 100644 tests/Tests/_VerifySnapshots/BoolQueryUsageTests.VerifyDescriptorJson.verified.txt create mode 100644 tests/Tests/_VerifySnapshots/BoolQueryUsageTests.VerifyInitializerJson.verified.txt create mode 100644 tests/Tests/_VerifySnapshots/SortUsageTests.VerifyDescriptorJson.verified.txt create mode 100644 tests/Tests/_VerifySnapshots/SortUsageTests.VerifyInitializerJson.verified.txt diff --git a/src/Elastic.Clients.Elasticsearch/Types/SortOptions.cs b/src/Elastic.Clients.Elasticsearch/Types/SortOptions.cs index 9fa7e91b1a2..713272d31ff 100644 --- a/src/Elastic.Clients.Elasticsearch/Types/SortOptions.cs +++ b/src/Elastic.Clients.Elasticsearch/Types/SortOptions.cs @@ -18,7 +18,7 @@ public partial class SortOptions internal sealed class SortOptionsConverter : JsonConverter { // We manually define this converter since we simplify SortCombinations union from the spec as SortOptions instance. - // This requires a custom read method to handle deserialisation of the potential union JSON as specified. + // This requires a custom read method to handle deserialization of the potential union JSON as specified. public override SortOptions Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { @@ -53,6 +53,13 @@ public override SortOptions Read(ref Utf8JsonReader reader, Type typeToConvert, return new SortOptions(propertyName, variant); } + if (propertyName == "_geo_distance") + { + var variant = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new SortOptions(propertyName, variant); + } + // For field sorts, the property name will be the field name { var variant = JsonSerializer.Deserialize(ref reader, options); @@ -104,6 +111,9 @@ public override void Write(Utf8JsonWriter writer, SortOptions value, JsonSeriali case "_script": JsonSerializer.Serialize(writer, (ScriptSort)value.Variant, options); break; + case "_geo_distance": + JsonSerializer.Serialize(writer, (GeoDistanceSort)value.Variant, options); + break; default: JsonSerializer.Serialize(writer, (FieldSort)value.Variant, options); break; diff --git a/src/Elastic.Clients.Elasticsearch/_Generated/Types/Enums/Enums.NoNamespace.g.cs b/src/Elastic.Clients.Elasticsearch/_Generated/Types/Enums/Enums.NoNamespace.g.cs index 27e13517006..66fa9624241 100644 --- a/src/Elastic.Clients.Elasticsearch/_Generated/Types/Enums/Enums.NoNamespace.g.cs +++ b/src/Elastic.Clients.Elasticsearch/_Generated/Types/Enums/Enums.NoNamespace.g.cs @@ -451,6 +451,48 @@ public override void Write(Utf8JsonWriter writer, FieldSortNumericType value, Js } } +[JsonConverter(typeof(GeoDistanceTypeConverter))] +public enum GeoDistanceType +{ + [EnumMember(Value = "plane")] + Plane, + [EnumMember(Value = "arc")] + Arc +} + +internal sealed class GeoDistanceTypeConverter : JsonConverter +{ + public override GeoDistanceType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var enumString = reader.GetString(); + switch (enumString) + { + case "plane": + return GeoDistanceType.Plane; + case "arc": + return GeoDistanceType.Arc; + } + + ThrowHelper.ThrowJsonException(); + return default; + } + + public override void Write(Utf8JsonWriter writer, GeoDistanceType value, JsonSerializerOptions options) + { + switch (value) + { + case GeoDistanceType.Plane: + writer.WriteStringValue("plane"); + return; + case GeoDistanceType.Arc: + writer.WriteStringValue("arc"); + return; + } + + writer.WriteNullValue(); + } +} + [JsonConverter(typeof(HealthStatusConverter))] public enum HealthStatus { diff --git a/src/Elastic.Clients.Elasticsearch/_Generated/Types/GeoDistanceSort.g.cs b/src/Elastic.Clients.Elasticsearch/_Generated/Types/GeoDistanceSort.g.cs new file mode 100644 index 00000000000..bd688df19ca --- /dev/null +++ b/src/Elastic.Clients.Elasticsearch/_Generated/Types/GeoDistanceSort.g.cs @@ -0,0 +1,378 @@ +// 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. +// +// ███╗ ██╗ ██████╗ ████████╗██╗ ██████╗███████╗ +// ████╗ ██║██╔═══██╗╚══██╔══╝██║██╔════╝██╔════╝ +// ██╔██╗ ██║██║ ██║ ██║ ██║██║ █████╗ +// ██║╚██╗██║██║ ██║ ██║ ██║██║ ██╔══╝ +// ██║ ╚████║╚██████╔╝ ██║ ██║╚██████╗███████╗ +// ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝ +// ------------------------------------------------ +// +// This file is automatically generated. +// Please do not edit these files manually. +// +// ------------------------------------------------ + +using Elastic.Clients.Elasticsearch.Fluent; +using Elastic.Clients.Elasticsearch.Serialization; +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Text.Json; +using System.Text.Json.Serialization; + +#nullable restore +namespace Elastic.Clients.Elasticsearch; +internal sealed partial class GeoDistanceSortConverter : JsonConverter +{ + public override GeoDistanceSort Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException("Unexpected JSON detected."); + var variant = new GeoDistanceSort(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) + { + if (reader.TokenType == JsonTokenType.PropertyName) + { + var property = reader.GetString(); + if (property == "distance_type") + { + variant.DistanceType = JsonSerializer.Deserialize(ref reader, options); + continue; + } + + if (property == "ignore_unmapped") + { + variant.IgnoreUnmapped = JsonSerializer.Deserialize(ref reader, options); + continue; + } + + if (property == "mode") + { + variant.Mode = JsonSerializer.Deserialize(ref reader, options); + continue; + } + + if (property == "order") + { + variant.Order = JsonSerializer.Deserialize(ref reader, options); + continue; + } + + if (property == "unit") + { + variant.Unit = JsonSerializer.Deserialize(ref reader, options); + continue; + } + + variant.Field = property; + reader.Read(); + variant.Location = JsonSerializer.Deserialize>(ref reader, options); + } + } + + return variant; + } + + public override void Write(Utf8JsonWriter writer, GeoDistanceSort value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + if (value.Field is not null && value.Location is not null) + { + if (!options.TryGetClientSettings(out var settings)) + { + ThrowHelper.ThrowJsonExceptionForMissingSettings(); + } + + var propertyName = settings.Inferrer.Field(value.Field); + writer.WritePropertyName(propertyName); + JsonSerializer.Serialize(writer, value.Location, options); + } + + if (value.DistanceType is not null) + { + writer.WritePropertyName("distance_type"); + JsonSerializer.Serialize(writer, value.DistanceType, options); + } + + if (value.IgnoreUnmapped.HasValue) + { + writer.WritePropertyName("ignore_unmapped"); + writer.WriteBooleanValue(value.IgnoreUnmapped.Value); + } + + if (value.Mode is not null) + { + writer.WritePropertyName("mode"); + JsonSerializer.Serialize(writer, value.Mode, options); + } + + if (value.Order is not null) + { + writer.WritePropertyName("order"); + JsonSerializer.Serialize(writer, value.Order, options); + } + + if (value.Unit is not null) + { + writer.WritePropertyName("unit"); + JsonSerializer.Serialize(writer, value.Unit, options); + } + + writer.WriteEndObject(); + } +} + +[JsonConverter(typeof(GeoDistanceSortConverter))] +public sealed partial class GeoDistanceSort +{ + public Elastic.Clients.Elasticsearch.GeoDistanceType? DistanceType { get; set; } + + public Elastic.Clients.Elasticsearch.Field Field { get; set; } + + public bool? IgnoreUnmapped { get; set; } + + public ICollection Location { get; set; } + + public Elastic.Clients.Elasticsearch.SortMode? Mode { get; set; } + + public Elastic.Clients.Elasticsearch.SortOrder? Order { get; set; } + + public Elastic.Clients.Elasticsearch.DistanceUnit? Unit { get; set; } + + public static implicit operator SortOptions(GeoDistanceSort geoDistanceSort) => SortOptions.GeoDistance(geoDistanceSort); +} + +public sealed partial class GeoDistanceSortDescriptor : SerializableDescriptor> +{ + internal GeoDistanceSortDescriptor(Action> configure) => configure.Invoke(this); + public GeoDistanceSortDescriptor() : base() + { + } + + private Elastic.Clients.Elasticsearch.GeoDistanceType? DistanceTypeValue { get; set; } + + private bool? IgnoreUnmappedValue { get; set; } + + private Elastic.Clients.Elasticsearch.SortMode? ModeValue { get; set; } + + private Elastic.Clients.Elasticsearch.SortOrder? OrderValue { get; set; } + + private Elastic.Clients.Elasticsearch.DistanceUnit? UnitValue { get; set; } + + private Elastic.Clients.Elasticsearch.Field FieldValue { get; set; } + + private ICollection LocationValue { get; set; } + + public GeoDistanceSortDescriptor DistanceType(Elastic.Clients.Elasticsearch.GeoDistanceType? distanceType) + { + DistanceTypeValue = distanceType; + return Self; + } + + public GeoDistanceSortDescriptor IgnoreUnmapped(bool? ignoreUnmapped = true) + { + IgnoreUnmappedValue = ignoreUnmapped; + return Self; + } + + public GeoDistanceSortDescriptor Mode(Elastic.Clients.Elasticsearch.SortMode? mode) + { + ModeValue = mode; + return Self; + } + + public GeoDistanceSortDescriptor Order(Elastic.Clients.Elasticsearch.SortOrder? order) + { + OrderValue = order; + return Self; + } + + public GeoDistanceSortDescriptor Unit(Elastic.Clients.Elasticsearch.DistanceUnit? unit) + { + UnitValue = unit; + return Self; + } + + public GeoDistanceSortDescriptor Location(ICollection location) + { + LocationValue = location; + return Self; + } + + public GeoDistanceSortDescriptor Field(Elastic.Clients.Elasticsearch.Field field) + { + FieldValue = field; + return Self; + } + + public GeoDistanceSortDescriptor Field(Expression> field) + { + FieldValue = field; + return Self; + } + + protected override void Serialize(Utf8JsonWriter writer, JsonSerializerOptions options, IElasticsearchClientSettings settings) + { + writer.WriteStartObject(); + if (FieldValue is not null && LocationValue is not null) + { + var propertyName = settings.Inferrer.Field(FieldValue); + writer.WritePropertyName(propertyName); + JsonSerializer.Serialize(writer, LocationValue, options); + } + + if (DistanceTypeValue is not null) + { + writer.WritePropertyName("distance_type"); + JsonSerializer.Serialize(writer, DistanceTypeValue, options); + } + + if (IgnoreUnmappedValue.HasValue) + { + writer.WritePropertyName("ignore_unmapped"); + writer.WriteBooleanValue(IgnoreUnmappedValue.Value); + } + + if (ModeValue is not null) + { + writer.WritePropertyName("mode"); + JsonSerializer.Serialize(writer, ModeValue, options); + } + + if (OrderValue is not null) + { + writer.WritePropertyName("order"); + JsonSerializer.Serialize(writer, OrderValue, options); + } + + if (UnitValue is not null) + { + writer.WritePropertyName("unit"); + JsonSerializer.Serialize(writer, UnitValue, options); + } + + writer.WriteEndObject(); + } +} + +public sealed partial class GeoDistanceSortDescriptor : SerializableDescriptor +{ + internal GeoDistanceSortDescriptor(Action configure) => configure.Invoke(this); + public GeoDistanceSortDescriptor() : base() + { + } + + private Elastic.Clients.Elasticsearch.GeoDistanceType? DistanceTypeValue { get; set; } + + private bool? IgnoreUnmappedValue { get; set; } + + private Elastic.Clients.Elasticsearch.SortMode? ModeValue { get; set; } + + private Elastic.Clients.Elasticsearch.SortOrder? OrderValue { get; set; } + + private Elastic.Clients.Elasticsearch.DistanceUnit? UnitValue { get; set; } + + private Elastic.Clients.Elasticsearch.Field FieldValue { get; set; } + + private ICollection LocationValue { get; set; } + + public GeoDistanceSortDescriptor DistanceType(Elastic.Clients.Elasticsearch.GeoDistanceType? distanceType) + { + DistanceTypeValue = distanceType; + return Self; + } + + public GeoDistanceSortDescriptor IgnoreUnmapped(bool? ignoreUnmapped = true) + { + IgnoreUnmappedValue = ignoreUnmapped; + return Self; + } + + public GeoDistanceSortDescriptor Mode(Elastic.Clients.Elasticsearch.SortMode? mode) + { + ModeValue = mode; + return Self; + } + + public GeoDistanceSortDescriptor Order(Elastic.Clients.Elasticsearch.SortOrder? order) + { + OrderValue = order; + return Self; + } + + public GeoDistanceSortDescriptor Unit(Elastic.Clients.Elasticsearch.DistanceUnit? unit) + { + UnitValue = unit; + return Self; + } + + public GeoDistanceSortDescriptor Location(ICollection location) + { + LocationValue = location; + return Self; + } + + public GeoDistanceSortDescriptor Field(Elastic.Clients.Elasticsearch.Field field) + { + FieldValue = field; + return Self; + } + + public GeoDistanceSortDescriptor Field(Expression> field) + { + FieldValue = field; + return Self; + } + + public GeoDistanceSortDescriptor Field(Expression> field) + { + FieldValue = field; + return Self; + } + + protected override void Serialize(Utf8JsonWriter writer, JsonSerializerOptions options, IElasticsearchClientSettings settings) + { + writer.WriteStartObject(); + if (FieldValue is not null && LocationValue is not null) + { + var propertyName = settings.Inferrer.Field(FieldValue); + writer.WritePropertyName(propertyName); + JsonSerializer.Serialize(writer, LocationValue, options); + } + + if (DistanceTypeValue is not null) + { + writer.WritePropertyName("distance_type"); + JsonSerializer.Serialize(writer, DistanceTypeValue, options); + } + + if (IgnoreUnmappedValue.HasValue) + { + writer.WritePropertyName("ignore_unmapped"); + writer.WriteBooleanValue(IgnoreUnmappedValue.Value); + } + + if (ModeValue is not null) + { + writer.WritePropertyName("mode"); + JsonSerializer.Serialize(writer, ModeValue, options); + } + + if (OrderValue is not null) + { + writer.WritePropertyName("order"); + JsonSerializer.Serialize(writer, OrderValue, options); + } + + if (UnitValue is not null) + { + writer.WritePropertyName("unit"); + JsonSerializer.Serialize(writer, UnitValue, options); + } + + writer.WriteEndObject(); + } +} \ No newline at end of file diff --git a/src/Elastic.Clients.Elasticsearch/_Generated/Types/SortOptions.g.cs b/src/Elastic.Clients.Elasticsearch/_Generated/Types/SortOptions.g.cs index b75cde29fff..b92d464bb08 100644 --- a/src/Elastic.Clients.Elasticsearch/_Generated/Types/SortOptions.g.cs +++ b/src/Elastic.Clients.Elasticsearch/_Generated/Types/SortOptions.g.cs @@ -58,6 +58,7 @@ internal SortOptions(Elastic.Clients.Elasticsearch.Field field, object variant) internal Elastic.Clients.Elasticsearch.Field? AdditionalPropertyName { get; } public static SortOptions Doc(Elastic.Clients.Elasticsearch.ScoreSort scoreSort) => new SortOptions("_doc", scoreSort); + public static SortOptions GeoDistance(Elastic.Clients.Elasticsearch.GeoDistanceSort geoDistanceSort) => new SortOptions("_geo_distance", geoDistanceSort); public static SortOptions Score(Elastic.Clients.Elasticsearch.ScoreSort scoreSort) => new SortOptions("_score", scoreSort); public static SortOptions Script(Elastic.Clients.Elasticsearch.ScriptSort scriptSort) => new SortOptions("_script", scriptSort); public static SortOptions Field(Elastic.Clients.Elasticsearch.Field field, Elastic.Clients.Elasticsearch.FieldSort fieldSort) => new SortOptions(field, fieldSort); @@ -131,6 +132,8 @@ private SortOptionsDescriptor Set(Elastic.Clients.Elasticsearch.Field public SortOptionsDescriptor Doc(ScoreSort scoreSort) => Set(scoreSort, "_doc"); public SortOptionsDescriptor Doc(Action configure) => Set(configure, "_doc"); + public SortOptionsDescriptor GeoDistance(GeoDistanceSort geoDistanceSort) => Set(geoDistanceSort, "_geo_distance"); + public SortOptionsDescriptor GeoDistance(Action> configure) => Set(configure, "_geo_distance"); public SortOptionsDescriptor Score(ScoreSort scoreSort) => Set(scoreSort, "_score"); public SortOptionsDescriptor Score(Action configure) => Set(configure, "_score"); public SortOptionsDescriptor Script(ScriptSort scriptSort) => Set(scriptSort, "_script"); @@ -250,6 +253,9 @@ private SortOptionsDescriptor Set(Elastic.Clients.Elasticsearch.FieldSort varian public SortOptionsDescriptor Doc(ScoreSort scoreSort) => Set(scoreSort, "_doc"); public SortOptionsDescriptor Doc(Action configure) => Set(configure, "_doc"); + public SortOptionsDescriptor GeoDistance(GeoDistanceSort geoDistanceSort) => Set(geoDistanceSort, "_geo_distance"); + public SortOptionsDescriptor GeoDistance(Action configure) => Set(configure, "_geo_distance"); + public SortOptionsDescriptor GeoDistance(Action> configure) => Set(configure, "_geo_distance"); public SortOptionsDescriptor Score(ScoreSort scoreSort) => Set(scoreSort, "_score"); public SortOptionsDescriptor Score(Action configure) => Set(configure, "_score"); public SortOptionsDescriptor Script(ScriptSort scriptSort) => Set(scriptSort, "_script"); diff --git a/tests/Tests/Framework/EndpointTests/ApiTestBase.cs b/tests/Tests/Framework/EndpointTests/ApiTestBase.cs index 32d75381713..9a52b18a0dc 100644 --- a/tests/Tests/Framework/EndpointTests/ApiTestBase.cs +++ b/tests/Tests/Framework/EndpointTests/ApiTestBase.cs @@ -36,6 +36,26 @@ protected ApiTestBase(TCluster cluster, EndpointUsage usage) : base(cluster, usa protected abstract HttpMethod ExpectHttpMethod { get; } protected abstract string ExpectedUrlPathAndQuery { get; } + // TODO - It would be useful to verify that the JSON is equivalent, but using a string + // comparison is too brittle due to potential for elements to be ordered differently. + + //[U] + //protected virtual void VerifyInitializerAndDescriptorProduceIdenticalJson() + //{ + // if (VerifyJson) + // { + + // var initializerJson = SerializeUsingClient(Initializer); + + // var descriptor = NewDescriptor(); + // Fluent?.Invoke(descriptor); + + // var descriptorJson = SerializeUsingClient(descriptor); + + // initializerJson.Equals(descriptorJson).Should().BeTrue(); + // } + //} + [U] protected virtual async Task VerifyInitializerJson() { diff --git a/tests/Tests/QueryDsl/Compound/BoolQueryUsageTests.cs b/tests/Tests/QueryDsl/Compound/BoolQueryUsageTests.cs new file mode 100644 index 00000000000..db2c1e18586 --- /dev/null +++ b/tests/Tests/QueryDsl/Compound/BoolQueryUsageTests.cs @@ -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 Elastic.Clients.Elasticsearch.QueryDsl; +using Tests.Core.ManagedElasticsearch.Clusters; +using Tests.Domain; +using Tests.Framework.EndpointTests.TestState; + +namespace Tests.QueryDsl.Compound; + +public class BoolQueryUsageTests : QueryDslUsageTestsBase +{ + public BoolQueryUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) + { + } + + protected override bool VerifyJson => true; + + protected override Query QueryInitializer => new BoolQuery + { + Must = new Query[] + { + new MatchAllQuery() + }, + Should = new Query[] + { + new TermQuery(Infer.Field(f => f.Name)) { Value = "Steve" }, + new TermQuery(Infer.Field(f => f.Name)) { Value = "David" } + } + }; + + protected override QueryDescriptor QueryFluent(QueryDescriptor queryDescriptor) => + queryDescriptor + .Bool(b => b + .Must(m => m.MatchAll()) + .Should( + s => s.Term(f => f.Name, "Steve"), + s => s.Term(f => f.Name, "David"))); +} diff --git a/tests/Tests/Search/Search/SearchUsageTestBase.cs b/tests/Tests/Search/Search/SearchUsageTestBase.cs index fa0badeaf85..0a42d095a8e 100644 --- a/tests/Tests/Search/Search/SearchUsageTestBase.cs +++ b/tests/Tests/Search/Search/SearchUsageTestBase.cs @@ -19,8 +19,6 @@ public abstract class SearchUsageTestBase Value = Project.TypeName }; - protected object ProjectFilterExpectedJson = new { term = new { type = new { value = Project.TypeName } } }; - protected SearchUsageTestBase(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { } protected override bool ExpectIsValid => true; diff --git a/tests/Tests/Search/Search/SortUsageTests.cs b/tests/Tests/Search/Search/SortUsageTests.cs index d7f5741566d..deef015bffa 100644 --- a/tests/Tests/Search/Search/SortUsageTests.cs +++ b/tests/Tests/Search/Search/SortUsageTests.cs @@ -13,23 +13,27 @@ public class SortUsageTests : SearchUsageTestBase { public SortUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { } - protected override object ExpectJson => - new - { - sort = new object[] - { - new { startedOn = new { order = "asc" } }, - new { name = new { order = "desc" } } - } - }; + protected override bool VerifyJson => true; + + // TODO: Port other tests from NEST - // TODO - Update once we code-generate the fluent syntax + // NOTE: The descriptor syntax is usable but more complex than NEST which provides a collection-based promise. + // TODO: Consider updating code-gen to produce simpler descriptor syntax for this case. protected override Action> Fluent => s => s - .Sort(new[] - { - SortOptions.Field(Infer.Field(f => f.StartedOn), new FieldSort { Order = SortOrder.Asc }), - SortOptions.Field(Infer.Field(f => f.Name), new FieldSort { Order = SortOrder.Desc }) - }); + .Sort( + s => s.Field(f => f.StartedOn, fs => fs.Order(SortOrder.Asc)), + s => s.Field(f => f.Name, fs => fs.Order(SortOrder.Desc)), + s => s.GeoDistance(f => f + .Field(fld => fld.LocationPoint) + .DistanceType(GeoDistanceType.Arc) + .Order(SortOrder.Asc) + .Unit(DistanceUnit.Centimeters) + .Mode(SortMode.Min) + .Location(new [] + { + GeoLocation.LatitudeLongitude(new () { Lat = 70, Lon = -70 }), + GeoLocation.LatitudeLongitude(new () { Lat = -12, Lon = 12 }) + }))); protected override SearchRequest Initializer => new() @@ -37,7 +41,20 @@ public SortUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(clust Sort = new[] { SortOptions.Field(Infer.Field(f => f.StartedOn), new FieldSort { Order = SortOrder.Asc }), - SortOptions.Field(Infer.Field(f => f.Name), new FieldSort { Order = SortOrder.Desc }) + SortOptions.Field(Infer.Field(f => f.Name), new FieldSort { Order = SortOrder.Desc }), + SortOptions.GeoDistance(new GeoDistanceSort + { + Field = Infer.Field(f => f.LocationPoint), + DistanceType = GeoDistanceType.Arc, + Order = SortOrder.Asc, + Unit = DistanceUnit.Centimeters, + Mode = SortMode.Min, + Location = new [] + { + GeoLocation.LatitudeLongitude(new () { Lat = 70, Lon = -70 }), + GeoLocation.LatitudeLongitude(new () { Lat = -12, Lon = 12 }) + } + }) } }; } diff --git a/tests/Tests/_VerifySnapshots/BoolQueryUsageTests.VerifyDescriptorJson.verified.txt b/tests/Tests/_VerifySnapshots/BoolQueryUsageTests.VerifyDescriptorJson.verified.txt new file mode 100644 index 00000000000..eb2851fa4b4 --- /dev/null +++ b/tests/Tests/_VerifySnapshots/BoolQueryUsageTests.VerifyDescriptorJson.verified.txt @@ -0,0 +1,25 @@ +{ + query: { + bool: { + must: { + match_all: {} + }, + should: [ + { + term: { + name: { + value: Steve + } + } + }, + { + term: { + name: { + value: David + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/tests/Tests/_VerifySnapshots/BoolQueryUsageTests.VerifyInitializerJson.verified.txt b/tests/Tests/_VerifySnapshots/BoolQueryUsageTests.VerifyInitializerJson.verified.txt new file mode 100644 index 00000000000..eb2851fa4b4 --- /dev/null +++ b/tests/Tests/_VerifySnapshots/BoolQueryUsageTests.VerifyInitializerJson.verified.txt @@ -0,0 +1,25 @@ +{ + query: { + bool: { + must: { + match_all: {} + }, + should: [ + { + term: { + name: { + value: Steve + } + } + }, + { + term: { + name: { + value: David + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/tests/Tests/_VerifySnapshots/SortUsageTests.VerifyDescriptorJson.verified.txt b/tests/Tests/_VerifySnapshots/SortUsageTests.VerifyDescriptorJson.verified.txt new file mode 100644 index 00000000000..9f657169f57 --- /dev/null +++ b/tests/Tests/_VerifySnapshots/SortUsageTests.VerifyDescriptorJson.verified.txt @@ -0,0 +1,32 @@ +{ + sort: [ + { + startedOn: { + order: asc + } + }, + { + name: { + order: desc + } + }, + { + _geo_distance: { + distance_type: arc, + locationPoint: [ + { + lat: 70, + lon: -70 + }, + { + lat: -12, + lon: 12 + } + ], + mode: min, + order: asc, + unit: cm + } + } + ] +} \ No newline at end of file diff --git a/tests/Tests/_VerifySnapshots/SortUsageTests.VerifyInitializerJson.verified.txt b/tests/Tests/_VerifySnapshots/SortUsageTests.VerifyInitializerJson.verified.txt new file mode 100644 index 00000000000..9f657169f57 --- /dev/null +++ b/tests/Tests/_VerifySnapshots/SortUsageTests.VerifyInitializerJson.verified.txt @@ -0,0 +1,32 @@ +{ + sort: [ + { + startedOn: { + order: asc + } + }, + { + name: { + order: desc + } + }, + { + _geo_distance: { + distance_type: arc, + locationPoint: [ + { + lat: 70, + lon: -70 + }, + { + lat: -12, + lon: 12 + } + ], + mode: min, + order: asc, + unit: cm + } + } + ] +} \ No newline at end of file