diff --git a/src/Nest/Mapping/DynamicTemplate/SingleMapping.cs b/src/Nest/Mapping/DynamicTemplate/SingleMapping.cs index bca5839f97c..f8d08f12716 100644 --- a/src/Nest/Mapping/DynamicTemplate/SingleMapping.cs +++ b/src/Nest/Mapping/DynamicTemplate/SingleMapping.cs @@ -141,6 +141,10 @@ public IProperty SearchAsYouType(Func, ISea public IProperty ConstantKeyword(Func, IConstantKeywordProperty> selector) => selector?.Invoke(new ConstantKeywordPropertyDescriptor()); + /// + public IProperty Wildcard(Func, IWildcardProperty> selector) => + selector?.Invoke(new WildcardPropertyDescriptor()); + #pragma warning disable CS3001 // Argument type is not CLS-compliant public IProperty Scalar(Expression> field, Func, INumberProperty> selector = null) => selector.InvokeOrDefault(new NumberPropertyDescriptor().Name(field).Type(NumberType.Integer)); diff --git a/src/Nest/Mapping/Types/FieldType.cs b/src/Nest/Mapping/Types/FieldType.cs index acca3bddb36..25c34f10b23 100644 --- a/src/Nest/Mapping/Types/FieldType.cs +++ b/src/Nest/Mapping/Types/FieldType.cs @@ -148,6 +148,9 @@ public enum FieldType Histogram, [EnumMember(Value = "constant_keyword")] - ConstantKeyword + ConstantKeyword, + + [EnumMember(Value = "wildcard")] + Wildcard, } } diff --git a/src/Nest/Mapping/Types/Properties.cs b/src/Nest/Mapping/Types/Properties.cs index b942c3cac41..a56cb48978a 100644 --- a/src/Nest/Mapping/Types/Properties.cs +++ b/src/Nest/Mapping/Types/Properties.cs @@ -145,6 +145,9 @@ TReturnType Nested(Func, INestedProp /// TReturnType ConstantKeyword(Func, IConstantKeywordProperty> selector); + + /// + TReturnType Wildcard(Func, IWildcardProperty> selector); } public partial class PropertiesDescriptor where T : class @@ -229,6 +232,10 @@ public PropertiesDescriptor Object(Func ConstantKeyword(Func, IConstantKeywordProperty> selector) => SetProperty(selector); + /// + public PropertiesDescriptor Wildcard(Func, IWildcardProperty> selector) => + SetProperty(selector); + public PropertiesDescriptor Custom(IProperty customType) => SetProperty(customType); private PropertiesDescriptor SetProperty(Func selector) diff --git a/src/Nest/Mapping/Types/PropertyFormatter.cs b/src/Nest/Mapping/Types/PropertyFormatter.cs index b24eb2052c8..fcf91a0c105 100644 --- a/src/Nest/Mapping/Types/PropertyFormatter.cs +++ b/src/Nest/Mapping/Types/PropertyFormatter.cs @@ -98,6 +98,7 @@ public IProperty Deserialize(ref JsonReader reader, IJsonFormatterResolver forma case FieldType.Flattened: return Deserialize(ref segmentReader, formatterResolver); case FieldType.Histogram: return Deserialize(ref segmentReader, formatterResolver); case FieldType.ConstantKeyword: return Deserialize(ref segmentReader, formatterResolver); + case FieldType.Wildcard: return Deserialize(ref segmentReader, formatterResolver); case FieldType.None: // no "type" field in the property mapping, or FieldType enum could not be parsed from typeString return Deserialize(ref segmentReader, formatterResolver); @@ -209,6 +210,9 @@ public void Serialize(ref JsonWriter writer, IProperty value, IJsonFormatterReso case IConstantKeywordProperty constantKeywordProperty: Serialize(ref writer, constantKeywordProperty, formatterResolver); break; + case IWildcardProperty wildcardProperty: + Serialize(ref writer, wildcardProperty, formatterResolver); + break; case IGenericProperty genericProperty: Serialize(ref writer, genericProperty, formatterResolver); break; diff --git a/src/Nest/Mapping/Types/Specialized/Wildcard/WildcardAttribute.cs b/src/Nest/Mapping/Types/Specialized/Wildcard/WildcardAttribute.cs new file mode 100644 index 00000000000..8080b356b18 --- /dev/null +++ b/src/Nest/Mapping/Types/Specialized/Wildcard/WildcardAttribute.cs @@ -0,0 +1,26 @@ +// 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 + +namespace Nest +{ + /// + public class WildcardAttribute : ElasticsearchPropertyAttributeBase, IWildcardProperty + { + public WildcardAttribute() : base(FieldType.Wildcard) { } + + int? IWildcardProperty.IgnoreAbove { get; set; } + + private IWildcardProperty Self => this; + + /// + public int IgnoreAbove + { + get => Self.IgnoreAbove.GetValueOrDefault(2147483647); + set => Self.IgnoreAbove = value; + } + + /// + public string NullValue { get; set; } + } +} diff --git a/src/Nest/Mapping/Types/Specialized/Wildcard/WildcardProperty.cs b/src/Nest/Mapping/Types/Specialized/Wildcard/WildcardProperty.cs new file mode 100644 index 00000000000..5d2d66a1a40 --- /dev/null +++ b/src/Nest/Mapping/Types/Specialized/Wildcard/WildcardProperty.cs @@ -0,0 +1,62 @@ +// 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 System.Diagnostics; +using System.Runtime.Serialization; +using Elasticsearch.Net.Utf8Json; + +namespace Nest +{ + /// + /// A wildcard field stores values optimised for wildcard grep-like queries. + /// + /// Available in Elasticsearch 7.9.0+ with at least a basic license level + /// + [InterfaceDataContract] + public interface IWildcardProperty : IProperty + { + /// + /// Do not index any string longer than this value. Defaults to 2147483647 so that all values would be accepted. + /// + [DataMember(Name = "ignore_above")] + int? IgnoreAbove { get; set; } + + /// + /// Accepts a string value which is substituted for any explicit null values. Defaults to null, which means the field is treated as missing. + /// + [DataMember(Name ="null_value")] + string NullValue { get; set; } + } + + /// + [DebuggerDisplay("{DebugDisplay}")] + public class WildcardProperty : PropertyBase, IWildcardProperty + { + public WildcardProperty() : base(FieldType.Wildcard) { } + + /// + public int? IgnoreAbove { get; set; } + + /// + public string NullValue { get; set; } + } + + /// + [DebuggerDisplay("{DebugDisplay}")] + public class WildcardPropertyDescriptor + : PropertyDescriptorBase, IWildcardProperty, T>, IWildcardProperty + where T : class + { + public WildcardPropertyDescriptor() : base(FieldType.Wildcard) { } + + int? IWildcardProperty.IgnoreAbove { get; set; } + string IWildcardProperty.NullValue { get; set; } + + /// + public WildcardPropertyDescriptor IgnoreAbove(int? ignoreAbove) => Assign(ignoreAbove, (a, v) => a.IgnoreAbove = v); + + /// + public WildcardPropertyDescriptor NullValue(string nullValue) => Assign(nullValue, (a, v) => a.NullValue = v); + } +} diff --git a/tests/Tests/Mapping/Types/Specialized/Wildcard/WildcardAttributeTests.cs b/tests/Tests/Mapping/Types/Specialized/Wildcard/WildcardAttributeTests.cs new file mode 100644 index 00000000000..865834b84a5 --- /dev/null +++ b/tests/Tests/Mapping/Types/Specialized/Wildcard/WildcardAttributeTests.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.Elasticsearch.Xunit.XunitPlumbing; +using Nest; + +namespace Tests.Mapping.Types.Specialized.Wildcard +{ + public class WildcardTest + { + [Wildcard(IgnoreAbove = 512, NullValue = "foo")] + public string Full { get; set; } + + [Wildcard] + public string Simple { get; set; } + } + + [SkipVersion("<7.9.0", "introduced in 7.9.0")] + public class WildcardAttributeTests : AttributeTestsBase + { + protected override object ExpectJson => new + { + properties = new + { + full = new + { + type = "wildcard", + ignore_above = 512, + null_value = "foo" + }, + simple = new + { + type = "wildcard" + } + } + }; + } +} diff --git a/tests/Tests/Mapping/Types/Specialized/Wildcard/WildcardPropertyTests.cs b/tests/Tests/Mapping/Types/Specialized/Wildcard/WildcardPropertyTests.cs new file mode 100644 index 00000000000..11e7fe25a5f --- /dev/null +++ b/tests/Tests/Mapping/Types/Specialized/Wildcard/WildcardPropertyTests.cs @@ -0,0 +1,42 @@ +// 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 System; +using Elastic.Elasticsearch.Xunit.XunitPlumbing; +using Nest; +using Tests.Core.ManagedElasticsearch.Clusters; +using Tests.Domain; +using Tests.Framework.EndpointTests.TestState; + +namespace Tests.Mapping.Types.Specialized.Wildcard +{ + [SkipVersion("<7.9.0", "introduced in 7.9.0")] + public class WildcardPropertyTests : PropertyTestsBase + { + public WildcardPropertyTests(WritableCluster cluster, EndpointUsage usage) : base(cluster, usage) { } + + protected override object ExpectJson => new + { + properties = new + { + description = new + { + type = "wildcard" + } + } + }; + + protected override Func, IPromise> FluentProperties => f => f + .Wildcard(s => s + .Name(n => n.Description) + ); + + protected override IProperties InitializerProperties => new Properties + { + { + "description", new WildcardProperty() + } + }; + } +}