diff --git a/src/Nest/Aggregations/Bucket/IpRange/IpRange.cs b/src/Nest/Aggregations/Bucket/IpRange/IpRange.cs deleted file mode 100644 index a79beed37c7..00000000000 --- a/src/Nest/Aggregations/Bucket/IpRange/IpRange.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Newtonsoft.Json; - -namespace Nest -{ - [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - [JsonConverter(typeof(ReadAsTypeJsonConverter))] - public interface IIpRange - { - [JsonProperty("from")] - string From { get; set; } - - [JsonProperty("to")] - string To { get; set; } - - [JsonProperty("mask")] - string Mask { get; set; } - } - - public class IpRange : IIpRange - { - public string From { get; set; } - - public string To { get; set; } - - public string Mask { get; set; } - } - - public class IpRangeDescriptor - : DescriptorBase, IIpRange - { - string IIpRange.From { get; set; } - string IIpRange.Mask { get; set; } - string IIpRange.To { get; set; } - - public IpRangeDescriptor From(string from) => Assign(a => a.From = from); - public IpRangeDescriptor To(string to) => Assign(a => a.To = to); - public IpRangeDescriptor Mask(string mask) => Assign(a => a.Mask = mask); - } -} diff --git a/src/Nest/Aggregations/Bucket/IpRange/IpRangeAggregation.cs b/src/Nest/Aggregations/Bucket/IpRange/IpRangeAggregation.cs index e6c179393aa..8b723a35636 100644 --- a/src/Nest/Aggregations/Bucket/IpRange/IpRangeAggregation.cs +++ b/src/Nest/Aggregations/Bucket/IpRange/IpRangeAggregation.cs @@ -14,13 +14,13 @@ public interface IIpRangeAggregation : IBucketAggregation Field Field { get; set; } [JsonProperty("ranges")] - IEnumerable Ranges { get; set; } + IEnumerable Ranges { get; set; } } public class IpRangeAggregation : BucketAggregationBase, IIpRangeAggregation { public Field Field { get; set; } - public IEnumerable Ranges { get; set; } + public IEnumerable Ranges { get; set; } internal IpRangeAggregation() { } @@ -36,13 +36,13 @@ public class IpRangeAggregationDescriptor : { Field IIpRangeAggregation.Field { get; set; } - IEnumerable IIpRangeAggregation.Ranges { get; set; } + IEnumerable IIpRangeAggregation.Ranges { get; set; } public IpRangeAggregationDescriptor Field(Field field) => Assign(a => a.Field = field); public IpRangeAggregationDescriptor Field(Expression> field) => Assign(a => a.Field = field); - public IpRangeAggregationDescriptor Ranges(params Func[] ranges) => - Assign(a => a.Ranges = ranges?.Select(r => r(new IpRangeDescriptor()))); + public IpRangeAggregationDescriptor Ranges(params Func[] ranges) => + Assign(a => a.Ranges = ranges?.Select(r => r(new IpRangeAggregationRangeDescriptor()))); } } diff --git a/src/Nest/Aggregations/Bucket/IpRange/IpRangeAggregationRange.cs b/src/Nest/Aggregations/Bucket/IpRange/IpRangeAggregationRange.cs new file mode 100644 index 00000000000..5fed46719e5 --- /dev/null +++ b/src/Nest/Aggregations/Bucket/IpRange/IpRangeAggregationRange.cs @@ -0,0 +1,39 @@ +using Newtonsoft.Json; + +namespace Nest +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + [JsonConverter(typeof(ReadAsTypeJsonConverter))] + public interface IIpRangeAggregationRange + { + [JsonProperty("from")] + string From { get; set; } + + [JsonProperty("to")] + string To { get; set; } + + [JsonProperty("mask")] + string Mask { get; set; } + } + + public class IpRangeAggregationRange : IIpRangeAggregationRange + { + public string From { get; set; } + + public string To { get; set; } + + public string Mask { get; set; } + } + + public class IpRangeAggregationRangeDescriptor + : DescriptorBase, IIpRangeAggregationRange + { + string IIpRangeAggregationRange.From { get; set; } + string IIpRangeAggregationRange.Mask { get; set; } + string IIpRangeAggregationRange.To { get; set; } + + public IpRangeAggregationRangeDescriptor From(string from) => Assign(a => a.From = from); + public IpRangeAggregationRangeDescriptor To(string to) => Assign(a => a.To = to); + public IpRangeAggregationRangeDescriptor Mask(string mask) => Assign(a => a.Mask = mask); + } +} diff --git a/src/Nest/CommonOptions/Range/Ranges.cs b/src/Nest/CommonOptions/Range/Ranges.cs index e820dd05c70..dcbce7f9662 100644 --- a/src/Nest/CommonOptions/Range/Ranges.cs +++ b/src/Nest/CommonOptions/Range/Ranges.cs @@ -73,4 +73,18 @@ public class LongRange [JsonProperty("lt")] public long? LessThan { get; set; } } + public class IpRange + { + [JsonProperty("gte")] + public string GreaterThanOrEqualTo { get; set; } + + [JsonProperty("lte")] + public string LessThanOrEqualTo { get; set; } + + [JsonProperty("gt")] + public string GreaterThan { get; set; } + + [JsonProperty("lt")] + public string LessThan { get; set; } + } } diff --git a/src/Nest/Mapping/DynamicTemplate/SingleMapping.cs b/src/Nest/Mapping/DynamicTemplate/SingleMapping.cs index 2e99c2f8541..92f1435d47c 100644 --- a/src/Nest/Mapping/DynamicTemplate/SingleMapping.cs +++ b/src/Nest/Mapping/DynamicTemplate/SingleMapping.cs @@ -73,6 +73,9 @@ public IProperty IntegerRange(Func, IIntegerRa public IProperty LongRange(Func, ILongRangeProperty> selector) => selector?.Invoke(new LongRangePropertyDescriptor()); + public IProperty IpRange(Func, IIpRangeProperty> selector) => + selector?.Invoke(new IpRangePropertyDescriptor()); + public IProperty Join(Func, IJoinProperty> selector) => selector?.Invoke(new JoinPropertyDescriptor()); @@ -239,6 +242,8 @@ public IProperty Scalar(Expression> field, Func().Name(field)); public IProperty Scalar(Expression> field, Func, IFloatRangeProperty> selector = null) => selector.InvokeOrDefault(new FloatRangePropertyDescriptor().Name(field)); + public IProperty Scalar(Expression> field, Func, IIpRangeProperty> selector = null) => + selector.InvokeOrDefault(new IpRangePropertyDescriptor().Name(field)); #pragma warning restore CS3001 // Argument type is not CLS-compliant } } diff --git a/src/Nest/Mapping/Types/Core/Range/Ip/IpRangeAttribute.cs b/src/Nest/Mapping/Types/Core/Range/Ip/IpRangeAttribute.cs new file mode 100644 index 00000000000..b7c5fd9a824 --- /dev/null +++ b/src/Nest/Mapping/Types/Core/Range/Ip/IpRangeAttribute.cs @@ -0,0 +1,10 @@ +using System; +using Elasticsearch.Net; + +namespace Nest +{ + public class IpRangeAttribute : RangePropertyAttributeBase, IIpRangeProperty + { + public IpRangeAttribute() : base(RangeType.IpRange) { } + } +} diff --git a/src/Nest/Mapping/Types/Core/Range/Ip/IpRangeProperty.cs b/src/Nest/Mapping/Types/Core/Range/Ip/IpRangeProperty.cs new file mode 100644 index 00000000000..58cd7d286f4 --- /dev/null +++ b/src/Nest/Mapping/Types/Core/Range/Ip/IpRangeProperty.cs @@ -0,0 +1,21 @@ +using System; +using Elasticsearch.Net; +using Newtonsoft.Json; + +namespace Nest +{ + [JsonObject(MemberSerialization.OptIn)] + public interface IIpRangeProperty : IRangeProperty { } + + public class IpRangeProperty : RangePropertyBase, IIpRangeProperty + { + public IpRangeProperty() : base(RangeType.IpRange) { } + } + + public class IpRangePropertyDescriptor + : RangePropertyDescriptorBase, IIpRangeProperty, T>, IIpRangeProperty + where T : class + { + public IpRangePropertyDescriptor() : base(RangeType.IpRange) { } + } +} diff --git a/src/Nest/Mapping/Types/Core/Range/RangePropertyBase.cs b/src/Nest/Mapping/Types/Core/Range/RangePropertyBase.cs index 83897689ff2..33753fe88f8 100644 --- a/src/Nest/Mapping/Types/Core/Range/RangePropertyBase.cs +++ b/src/Nest/Mapping/Types/Core/Range/RangePropertyBase.cs @@ -49,11 +49,11 @@ public abstract class RangePropertyDescriptorBase protected RangePropertyDescriptorBase(RangeType type) : base(type.ToFieldType()) { } - /// + /// public TDescriptor Coerce(bool? coerce = true) => Assign(a => a.Coerce = coerce); - /// + /// public TDescriptor Boost(double? boost) => Assign(a => a.Boost = boost); - /// + /// public TDescriptor Index(bool? index = true) => Assign(a => a.Index = index); } } diff --git a/src/Nest/Mapping/Types/Core/Range/RangeType.cs b/src/Nest/Mapping/Types/Core/Range/RangeType.cs index be354793c22..a83d49f48d2 100644 --- a/src/Nest/Mapping/Types/Core/Range/RangeType.cs +++ b/src/Nest/Mapping/Types/Core/Range/RangeType.cs @@ -32,7 +32,12 @@ public enum RangeType /// A range of date values represented as unsigned 64-bit integer milliseconds elapsed since system epoch. /// [EnumMember(Value = "date_range")] - DateRange + DateRange, + /// + /// A range of ip values supporting either IPv4 or IPv6 (or mixed) addresses. + /// + [EnumMember(Value = "ip_range")] + IpRange } internal static class RangeTypeExtensions { @@ -45,6 +50,7 @@ public static FieldType ToFieldType(this RangeType rangeType) case RangeType.LongRange: return FieldType.LongRange; case RangeType.DoubleRange: return FieldType.DoubleRange; case RangeType.DateRange: return FieldType.DateRange; + case RangeType.IpRange: return FieldType.IpRange; default: throw new ArgumentOutOfRangeException(nameof(rangeType), rangeType, null); } diff --git a/src/Nest/Mapping/Types/FieldType.cs b/src/Nest/Mapping/Types/FieldType.cs index 0df93761c8a..fb3d451eaef 100644 --- a/src/Nest/Mapping/Types/FieldType.cs +++ b/src/Nest/Mapping/Types/FieldType.cs @@ -84,6 +84,8 @@ public enum FieldType DoubleRange, [EnumMember(Value = "date_range")] DateRange, + [EnumMember(Value = "ip_range")] + IpRange, [EnumMember(Value = "join")] Join, } diff --git a/src/Nest/Mapping/Types/Properties-Scalar.cs b/src/Nest/Mapping/Types/Properties-Scalar.cs index f4868727b0d..ebc3ce6ea35 100644 --- a/src/Nest/Mapping/Types/Properties-Scalar.cs +++ b/src/Nest/Mapping/Types/Properties-Scalar.cs @@ -100,6 +100,7 @@ public partial interface IPropertiesDescriptor TReturnType Scalar(Expression> field, Func, ILongRangeProperty> selector = null); TReturnType Scalar(Expression> field, Func, IIntegerRangeProperty> selector = null); TReturnType Scalar(Expression> field, Func, IFloatRangeProperty> selector = null); + TReturnType Scalar(Expression> field, Func, IIpRangeProperty> selector = null); #pragma warning restore CS3001 // Argument type is not CLS-compliant } @@ -269,6 +270,8 @@ public PropertiesDescriptor Scalar(Expression> field, F SetProperty(selector.InvokeOrDefault(new IntegerRangePropertyDescriptor().Name(field))); public PropertiesDescriptor Scalar(Expression> field, Func, IFloatRangeProperty> selector = null) => SetProperty(selector.InvokeOrDefault(new FloatRangePropertyDescriptor().Name(field))); + public PropertiesDescriptor Scalar(Expression> field, Func, IIpRangeProperty> selector = null) => + SetProperty(selector.InvokeOrDefault(new IpRangePropertyDescriptor().Name(field))); #pragma warning restore CS3001 // Argument type is not CLS-compliant } diff --git a/src/Nest/Mapping/Types/Properties.cs b/src/Nest/Mapping/Types/Properties.cs index 7bf08b41fc3..4cd602cb256 100644 --- a/src/Nest/Mapping/Types/Properties.cs +++ b/src/Nest/Mapping/Types/Properties.cs @@ -72,6 +72,7 @@ TReturnType Nested(Func, INestedProp TReturnType FloatRange(Func, IFloatRangeProperty> selector); TReturnType IntegerRange(Func, IIntegerRangeProperty> selector); TReturnType LongRange(Func, ILongRangeProperty> selector); + TReturnType IpRange(Func, IIpRangeProperty> selector); TReturnType Join(Func, IJoinProperty> selector); } @@ -127,6 +128,8 @@ public PropertiesDescriptor Nested(Func LongRange(Func, ILongRangeProperty> selector) => SetProperty(selector); + public PropertiesDescriptor IpRange(Func, IIpRangeProperty> selector) => SetProperty(selector); + public PropertiesDescriptor Join(Func, IJoinProperty> selector) => SetProperty(selector); public PropertiesDescriptor Custom(IProperty customType) => SetProperty(customType); diff --git a/src/Nest/Mapping/Types/PropertiesJsonConverter.cs b/src/Nest/Mapping/Types/PropertiesJsonConverter.cs index ed8efa2703e..0ce2fed83b8 100644 --- a/src/Nest/Mapping/Types/PropertiesJsonConverter.cs +++ b/src/Nest/Mapping/Types/PropertiesJsonConverter.cs @@ -54,14 +54,10 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist foreach (var p in o.Properties()) { var name = p.Name; - var po = p.First as JObject; - if (po == null) continue; - - var mapping = _elasticTypeConverter.ReadJson(po.CreateReader(), objectType, existingValue, serializer) as IProperty; - if (mapping == null) continue; + if (!(p.First is JObject po)) continue; + if (!(_elasticTypeConverter.ReadJson(po.CreateReader(), objectType, existingValue, serializer) is IProperty mapping)) continue; mapping.Name = name; - r.Add(name, mapping); } return r; diff --git a/src/Nest/Mapping/Types/PropertyJsonConverter.cs b/src/Nest/Mapping/Types/PropertyJsonConverter.cs index 8053fa2ddf8..816e710110c 100644 --- a/src/Nest/Mapping/Types/PropertyJsonConverter.cs +++ b/src/Nest/Mapping/Types/PropertyJsonConverter.cs @@ -11,16 +11,11 @@ internal class PropertyJsonConverter : JsonConverter public override bool CanConvert(Type objectType) => objectType == typeof(IProperty); - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotSupportedException(); - } - private TProperty ReadProperty(JObject j, JsonSerializer s) - where TProperty : IProperty - { - return FromJson.ReadAs(j.CreateReader(), s); - } + private TProperty ReadProperty(JObject j, JsonSerializer s) where TProperty : IProperty => + FromJson.ReadAs(j.CreateReader(), s); public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { @@ -64,6 +59,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist case FieldType.FloatRange: return ReadProperty(jObject, serializer); case FieldType.IntegerRange: return ReadProperty(jObject, serializer); case FieldType.LongRange: return ReadProperty(jObject, serializer); + case FieldType.IpRange: return ReadProperty(jObject, serializer); case FieldType.Join: return ReadProperty(jObject, serializer); case FieldType.None: break; diff --git a/src/Nest/Mapping/Visitor/PropertyWalker.cs b/src/Nest/Mapping/Visitor/PropertyWalker.cs index a17eff8ec02..f2fd331cbb3 100644 --- a/src/Nest/Mapping/Visitor/PropertyWalker.cs +++ b/src/Nest/Mapping/Visitor/PropertyWalker.cs @@ -155,6 +155,9 @@ private static IProperty InferProperty(PropertyInfo propertyInfo) if (type == typeof(LongRange)) return new LongRangeProperty(); + if (type == typeof(IpRange)) + return new IpRangeProperty(); + return new ObjectProperty(); } diff --git a/src/Tests/Aggregations/Bucket/IpRange/IpRangeAggregationUsageTests.cs b/src/Tests/Aggregations/Bucket/IpRange/IpRangeAggregationUsageTests.cs index 1e65c8c9ed8..1fc5bc40770 100644 --- a/src/Tests/Aggregations/Bucket/IpRange/IpRangeAggregationUsageTests.cs +++ b/src/Tests/Aggregations/Bucket/IpRange/IpRangeAggregationUsageTests.cs @@ -44,10 +44,10 @@ public IpRangeAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : ba new IpRangeAggregation("ip_ranges") { Field = Field((Project p) => p.LeadDeveloper.IpAddress), - Ranges = new List + Ranges = new List { - new Nest.IpRange {To = "127.0.0.1"}, - new Nest.IpRange {From = "127.0.0.1"} + new Nest.IpRangeAggregationRange {To = "127.0.0.1"}, + new Nest.IpRangeAggregationRange {From = "127.0.0.1"} } }; diff --git a/src/Tests/Framework/MockData/Ranges.cs b/src/Tests/Framework/MockData/Ranges.cs index 57e2858fdba..c578c5ec7bb 100644 --- a/src/Tests/Framework/MockData/Ranges.cs +++ b/src/Tests/Framework/MockData/Ranges.cs @@ -1,4 +1,5 @@ using System; +using System.Net; using Bogus; using Nest; @@ -11,6 +12,7 @@ public class Ranges public FloatRange Floats { get; set; } public IntegerRange Integers { get; set; } public LongRange Longs { get; set; } + public IpRange Ips { get; set; } //for deserialization public Ranges() { } @@ -23,6 +25,7 @@ private Ranges(Faker faker) SetFloats(faker, r); SetIntegers(faker, r); SetLongs(faker, r); + SetIps(faker, r); } private void SetDates(Faker faker, Func r) @@ -70,6 +73,27 @@ private void SetLongs(Faker faker, Func r) SwapAssign(r(), high, v => d.LessThan = v, v => d.LessThanOrEqualTo = v); this.Longs = d; } + private void SetIps(Faker faker, Func r) + { + var low = faker.Internet.Ip(); + var high = faker.Internet.Ip(); + var lowBytes = IPAddress.Parse(low).GetAddressBytes(); + var highBytes = IPAddress.Parse(high).GetAddressBytes(); + for (int i = 0; i < lowBytes.Length; i++) + { + if (lowBytes[i] > highBytes[i]) + { + var s = low; + low = high; + high = s; + break; + } + } + var d = new IpRange(); + SwapAssign(r(), low, v => d.GreaterThan = v, v => d.GreaterThanOrEqualTo = v); + SwapAssign(r(), high, v => d.LessThan = v, v => d.LessThanOrEqualTo = v); + this.Ips = d; + } private static void SwapAssign(bool b, T value, Action first, Action second) { diff --git a/src/Tests/Indices/MappingManagement/PutMapping/PutMappingApiTest.cs b/src/Tests/Indices/MappingManagement/PutMapping/PutMappingApiTest.cs index efe43b95717..009fa0e7f85 100644 --- a/src/Tests/Indices/MappingManagement/PutMapping/PutMappingApiTest.cs +++ b/src/Tests/Indices/MappingManagement/PutMapping/PutMappingApiTest.cs @@ -111,7 +111,8 @@ protected override LazyResponses ClientUsage() => Calls( doubles = new { type = "double_range" }, floats = new { type = "float_range" }, integers = new { type = "integer_range" }, - longs = new { type = "long_range" } + longs = new { type = "long_range" }, + ips = new { type = "ip_range" } }, type = "object" }, @@ -274,6 +275,7 @@ protected override LazyResponses ClientUsage() => Calls( {p => p.Floats, new FloatRangeProperty()}, {p => p.Integers, new IntegerRangeProperty()}, {p => p.Longs, new LongRangeProperty()}, + {p => p.Ips, new IpRangeProperty()}, } } }, diff --git a/src/Tests/Mapping/Types/Core/Range/IpRange/IpRangeAttributeTests.cs b/src/Tests/Mapping/Types/Core/Range/IpRange/IpRangeAttributeTests.cs new file mode 100644 index 00000000000..12f4ab7f7be --- /dev/null +++ b/src/Tests/Mapping/Types/Core/Range/IpRange/IpRangeAttributeTests.cs @@ -0,0 +1,26 @@ +using Nest; +using Tests.Framework; + +namespace Tests.Mapping.Types.Core.Range.IpRange +{ + public class IpRangeTest + { + [IpRange] + public Nest.IpRange Range { get; set; } + } + + [SkipVersion("<5.5.0", "ip range type is a new 5.5.0 feature")] + public class IpRangeAttributeTests : AttributeTestsBase + { + protected override object ExpectJson => new + { + properties = new + { + range = new + { + type = "ip_range" + } + } + }; + } +} diff --git a/src/Tests/Mapping/Types/Core/Range/IpRange/IpRangePropertyTests.cs b/src/Tests/Mapping/Types/Core/Range/IpRange/IpRangePropertyTests.cs new file mode 100644 index 00000000000..4a71de9515d --- /dev/null +++ b/src/Tests/Mapping/Types/Core/Range/IpRange/IpRangePropertyTests.cs @@ -0,0 +1,73 @@ +using System; +using Nest; +using Tests.Framework; +using Tests.Framework.Integration; +using Tests.Framework.ManagedElasticsearch.Clusters; +using Tests.Framework.MockData; + +namespace Tests.Mapping.Types.Core.Range.IpRange +{ + [SkipVersion("<5.5.0", "ip range type is a new 5.5.0 feature")] + public class IpRangePropertyTests : PropertyTestsBase + { + public IpRangePropertyTests(WritableCluster cluster, EndpointUsage usage) : base(cluster, usage) { } + + protected override object ExpectJson => new + { + properties = new + { + ranges = new + { + type = "object", + properties = new + { + ips = new + { + type = "ip_range", + store = true, + index = false, + boost = 1.5, + coerce = true + } + } + } + } + }; + + protected override Func, IPromise> FluentProperties => f => f + .Object(m => m + .Name(p => p.Ranges) + .Properties(props => props + .IpRange(n => n + .Name(p => p.Ips) + .Store() + .Index(false) + .Boost(1.5) + .Coerce() + ) + ) + ); + + + protected override IProperties InitializerProperties => new Properties + { + { + "ranges", new ObjectProperty + { + Properties = new Properties + { + { + "ips", new IpRangeProperty + { + Store = true, + Index = false, + Boost = 1.5, + Coerce = true + } + } + } + } + } + }; + } +}