From 146ce088c0c77e97993bbb8c360c33a94c318975 Mon Sep 17 00:00:00 2001 From: Steve Gordon Date: Tue, 6 Jul 2021 11:27:25 +0100 Subject: [PATCH] Add match_only_text field (#5801) --- .../Mapping/DynamicTemplate/SingleMapping.cs | 4 +++ .../MatchOnlyText/MatchOnlyTextAttribute.cs | 12 +++++++ .../MatchOnlyText/MatchOnlyTextProperty.cs | 36 +++++++++++++++++++ src/Nest/Mapping/Types/FieldType.cs | 8 ++++- src/Nest/Mapping/Types/Properties.cs | 6 ++++ src/Nest/Mapping/Types/PropertyFormatter.cs | 4 +++ src/Nest/Mapping/Visitor/IMappingVisitor.cs | 4 +++ src/Nest/Mapping/Visitor/IPropertyVisitor.cs | 2 ++ src/Nest/Mapping/Visitor/MappingWalker.cs | 6 ++++ .../Mapping/Visitor/NoopPropertyVisitor.cs | 9 +++-- .../FieldCapabilitiesResponse.cs | 1 + .../GetMapping/GetMappingApiTest.cs | 2 ++ .../DenseVector/DenseVectorPropertyTests.cs | 4 +-- .../MatchOnlyTextAttributeTests.cs | 21 +++++++++++ .../MatchOnlyTextPropertyTests.cs | 28 +++++++++++++++ 15 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 src/Nest/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextAttribute.cs create mode 100644 src/Nest/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextProperty.cs create mode 100644 tests/Tests/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextAttributeTests.cs create mode 100644 tests/Tests/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextPropertyTests.cs diff --git a/src/Nest/Mapping/DynamicTemplate/SingleMapping.cs b/src/Nest/Mapping/DynamicTemplate/SingleMapping.cs index 734ecd753d2..fb0adb69a90 100644 --- a/src/Nest/Mapping/DynamicTemplate/SingleMapping.cs +++ b/src/Nest/Mapping/DynamicTemplate/SingleMapping.cs @@ -157,6 +157,10 @@ public IProperty Generic(Func, IGenericProperty> se public IProperty DenseVector(Func, IDenseVectorProperty> selector) => selector?.Invoke(new DenseVectorPropertyDescriptor()); + /// + public IProperty MatchOnlyText(Func, IMatchOnlyTextProperty> selector) => + selector?.Invoke(new MatchOnlyTextPropertyDescriptor()); + #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/Core/MatchOnlyText/MatchOnlyTextAttribute.cs b/src/Nest/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextAttribute.cs new file mode 100644 index 00000000000..5849d44e0b7 --- /dev/null +++ b/src/Nest/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextAttribute.cs @@ -0,0 +1,12 @@ +// 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 MatchOnlyTextAttribute : ElasticsearchCorePropertyAttributeBase, IMatchOnlyTextProperty { + public MatchOnlyTextAttribute() : base(FieldType.MatchOnlyText) { } + protected MatchOnlyTextAttribute(FieldType fieldType) : base(fieldType) { } + private IMatchOnlyTextProperty Self => this; + } +} diff --git a/src/Nest/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextProperty.cs b/src/Nest/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextProperty.cs new file mode 100644 index 00000000000..4d6630f249b --- /dev/null +++ b/src/Nest/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextProperty.cs @@ -0,0 +1,36 @@ +// 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 Elasticsearch.Net.Utf8Json; + +namespace Nest +{ + /// + /// A variant of text that trades scoring and efficiency of positional queries for space efficiency. + /// This field effectively stores data the same way as a text field that only indexes documents + /// (index_options: docs) and disables norms (norms: false). + /// + /// MatchOnlyText fields are not used for sorting and seldom used for aggregations. + /// + [InterfaceDataContract] + public interface IMatchOnlyTextProperty : ICoreProperty + { + } + + /// + [DebuggerDisplay("{DebugDisplay}")] + public class MatchOnlyTextProperty : CorePropertyBase, IMatchOnlyTextProperty + { + public MatchOnlyTextProperty() : base(FieldType.MatchOnlyText) { } + } + + /// + public class MatchOnlyTextPropertyDescriptor + : CorePropertyDescriptorBase, IMatchOnlyTextProperty, T>, IMatchOnlyTextProperty + where T : class + { + public MatchOnlyTextPropertyDescriptor() : base(FieldType.MatchOnlyText) { } + } +} diff --git a/src/Nest/Mapping/Types/FieldType.cs b/src/Nest/Mapping/Types/FieldType.cs index 4cf096e6fa4..b22c653f1fb 100644 --- a/src/Nest/Mapping/Types/FieldType.cs +++ b/src/Nest/Mapping/Types/FieldType.cs @@ -170,6 +170,12 @@ public enum FieldType /// A dense_vector field stores dense vectors of float values. /// [EnumMember(Value = "dense_vector")] - DenseVector + DenseVector, + + /// + /// A variant of text that trades scoring and efficiency of positional queries for space efficiency. + /// + [EnumMember(Value = "match_only_text")] + MatchOnlyText, } } diff --git a/src/Nest/Mapping/Types/Properties.cs b/src/Nest/Mapping/Types/Properties.cs index 38087c6be36..0f0322a907d 100644 --- a/src/Nest/Mapping/Types/Properties.cs +++ b/src/Nest/Mapping/Types/Properties.cs @@ -157,6 +157,9 @@ TReturnType Nested(Func, INestedProp /// TReturnType DenseVector(Func, IDenseVectorProperty> selector); + + /// + TReturnType MatchOnlyText(Func, IMatchOnlyTextProperty> selector); } public partial class PropertiesDescriptor where T : class @@ -229,6 +232,9 @@ public PropertiesDescriptor ConstantKeyword(Func public PropertiesDescriptor LongRange(Func, ILongRangeProperty> selector) => SetProperty(selector); + /// + public PropertiesDescriptor MatchOnlyText(Func, IMatchOnlyTextProperty> selector) => SetProperty(selector); + /// public PropertiesDescriptor Murmur3Hash(Func, IMurmur3HashProperty> selector) => SetProperty(selector); diff --git a/src/Nest/Mapping/Types/PropertyFormatter.cs b/src/Nest/Mapping/Types/PropertyFormatter.cs index 1540508c7cb..35e327e1746 100644 --- a/src/Nest/Mapping/Types/PropertyFormatter.cs +++ b/src/Nest/Mapping/Types/PropertyFormatter.cs @@ -97,6 +97,7 @@ public IProperty Deserialize(ref JsonReader reader, IJsonFormatterResolver forma case FieldType.Wildcard: return Deserialize(ref segmentReader, formatterResolver); case FieldType.Version: return Deserialize(ref segmentReader, formatterResolver); case FieldType.DenseVector: return Deserialize(ref segmentReader, formatterResolver); + case FieldType.MatchOnlyText: 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); @@ -223,6 +224,9 @@ public void Serialize(ref JsonWriter writer, IProperty value, IJsonFormatterReso case IDenseVectorProperty denseVectorProperty: Serialize(ref writer, denseVectorProperty, formatterResolver); break; + case IMatchOnlyTextProperty matchOnlyTextProperty: + Serialize(ref writer, matchOnlyTextProperty, formatterResolver); + break; default: var formatter = formatterResolver.GetFormatter(); formatter.Serialize(ref writer, value, formatterResolver); diff --git a/src/Nest/Mapping/Visitor/IMappingVisitor.cs b/src/Nest/Mapping/Visitor/IMappingVisitor.cs index 265e983689b..f4e74f37de8 100644 --- a/src/Nest/Mapping/Visitor/IMappingVisitor.cs +++ b/src/Nest/Mapping/Visitor/IMappingVisitor.cs @@ -75,6 +75,8 @@ public interface IMappingVisitor void Visit(IVersionProperty property); void Visit(IDenseVectorProperty property); + + void Visit(IMatchOnlyTextProperty property); } public class NoopMappingVisitor : IMappingVisitor @@ -148,5 +150,7 @@ public virtual void Visit(IConstantKeywordProperty property) { } public virtual void Visit(IVersionProperty property) { } public virtual void Visit(IDenseVectorProperty property) { } + + public virtual void Visit(IMatchOnlyTextProperty property) { } } } diff --git a/src/Nest/Mapping/Visitor/IPropertyVisitor.cs b/src/Nest/Mapping/Visitor/IPropertyVisitor.cs index 838ffabf3a3..f58fc45630c 100644 --- a/src/Nest/Mapping/Visitor/IPropertyVisitor.cs +++ b/src/Nest/Mapping/Visitor/IPropertyVisitor.cs @@ -80,6 +80,8 @@ public interface IPropertyVisitor void Visit(IDenseVectorProperty type, PropertyInfo propertyInfo, ElasticsearchPropertyAttributeBase attribute); + void Visit(IMatchOnlyTextProperty type, PropertyInfo propertyInfo, ElasticsearchPropertyAttributeBase attribute); + IProperty Visit(PropertyInfo propertyInfo, ElasticsearchPropertyAttributeBase attribute); bool SkipProperty(PropertyInfo propertyInfo, ElasticsearchPropertyAttributeBase attribute); diff --git a/src/Nest/Mapping/Visitor/MappingWalker.cs b/src/Nest/Mapping/Visitor/MappingWalker.cs index 532634e83a2..4f79b6cb270 100644 --- a/src/Nest/Mapping/Visitor/MappingWalker.cs +++ b/src/Nest/Mapping/Visitor/MappingWalker.cs @@ -285,6 +285,12 @@ public void Accept(IProperties properties) _visitor.Visit(t); }); break; + case FieldType.MatchOnlyText: + Visit(field, t => + { + _visitor.Visit(t); + }); + break; case FieldType.None: continue; } diff --git a/src/Nest/Mapping/Visitor/NoopPropertyVisitor.cs b/src/Nest/Mapping/Visitor/NoopPropertyVisitor.cs index e8c6a686e76..11239494e02 100644 --- a/src/Nest/Mapping/Visitor/NoopPropertyVisitor.cs +++ b/src/Nest/Mapping/Visitor/NoopPropertyVisitor.cs @@ -80,6 +80,8 @@ public virtual void Visit(IVersionProperty type, PropertyInfo propertyInfo, Elas public virtual void Visit(IDenseVectorProperty type, PropertyInfo propertyInfo, ElasticsearchPropertyAttributeBase attribute) { } + public virtual void Visit(IMatchOnlyTextProperty type, PropertyInfo propertyInfo, ElasticsearchPropertyAttributeBase attribute) { } + public virtual IProperty Visit(PropertyInfo propertyInfo, ElasticsearchPropertyAttributeBase attribute) => null; public virtual void Visit(IProperty type, PropertyInfo propertyInfo, ElasticsearchPropertyAttributeBase attribute) @@ -185,8 +187,11 @@ public virtual void Visit(IProperty type, PropertyInfo propertyInfo, Elasticsear case IVersionProperty version: Visit(version, propertyInfo, attribute); break; - case IDenseVectorProperty version: - Visit(version, propertyInfo, attribute); + case IDenseVectorProperty denseVector: + Visit(denseVector, propertyInfo, attribute); + break; + case IMatchOnlyTextProperty matchOnlyText: + Visit(matchOnlyText, propertyInfo, attribute); break; } } diff --git a/src/Nest/Search/FieldCapabilities/FieldCapabilitiesResponse.cs b/src/Nest/Search/FieldCapabilities/FieldCapabilitiesResponse.cs index d552da55a42..1f75fa1bce0 100644 --- a/src/Nest/Search/FieldCapabilities/FieldCapabilitiesResponse.cs +++ b/src/Nest/Search/FieldCapabilities/FieldCapabilitiesResponse.cs @@ -56,6 +56,7 @@ public class FieldTypes : IsADictionaryBase public FieldCapabilities Keyword => BackingDictionary.TryGetValue("keyword", out var f) ? f : null; public FieldCapabilities Long => BackingDictionary.TryGetValue("long", out var f) ? f : null; public FieldCapabilities LongRange => BackingDictionary.TryGetValue("long_range", out var f) ? f : null; + public FieldCapabilities MatchOnlyText => BackingDictionary.TryGetValue("match_only_text", out var f) ? f : null; public FieldCapabilities Murmur3 => BackingDictionary.TryGetValue("murmur3", out var f) ? f : null; public FieldCapabilities Parent => BackingDictionary.TryGetValue("_parent", out var f) ? f : null; public FieldCapabilities ParentJoin => BackingDictionary.TryGetValue("_parent_join", out var f) ? f : null; diff --git a/tests/Tests/Indices/MappingManagement/GetMapping/GetMappingApiTest.cs b/tests/Tests/Indices/MappingManagement/GetMapping/GetMappingApiTest.cs index 281cf93909b..49f1c940a05 100644 --- a/tests/Tests/Indices/MappingManagement/GetMapping/GetMappingApiTest.cs +++ b/tests/Tests/Indices/MappingManagement/GetMapping/GetMappingApiTest.cs @@ -229,6 +229,8 @@ internal class TestVisitor : IMappingVisitor public void Visit(IDenseVectorProperty property) => Increment("dense_vector"); + public void Visit(IMatchOnlyTextProperty property) => Increment("match_only_text"); + private void Increment(string key) { if (!Counts.ContainsKey(key)) Counts.Add(key, 0); diff --git a/tests/Tests/Mapping/Types/Core/DenseVector/DenseVectorPropertyTests.cs b/tests/Tests/Mapping/Types/Core/DenseVector/DenseVectorPropertyTests.cs index 6641ffe6829..15589539833 100644 --- a/tests/Tests/Mapping/Types/Core/DenseVector/DenseVectorPropertyTests.cs +++ b/tests/Tests/Mapping/Types/Core/DenseVector/DenseVectorPropertyTests.cs @@ -12,9 +12,9 @@ namespace Tests.Mapping.Types.Core.DenseVector { [SkipVersion("<7.6.0", "Dense Vector property GA in 7.6.0")] - public class DenseVectorTests : PropertyTestsBase + public class DenseVectorPropertyTests : PropertyTestsBase { - public DenseVectorTests(WritableCluster cluster, EndpointUsage usage) : base(cluster, usage) { } + public DenseVectorPropertyTests(WritableCluster cluster, EndpointUsage usage) : base(cluster, usage) { } protected override object ExpectJson => new { properties = new { name = new { type = "dense_vector", dims = 2 } } }; diff --git a/tests/Tests/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextAttributeTests.cs b/tests/Tests/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextAttributeTests.cs new file mode 100644 index 00000000000..97af9062e80 --- /dev/null +++ b/tests/Tests/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextAttributeTests.cs @@ -0,0 +1,21 @@ +// 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.Core.MatchOnlyText +{ + public class MatchOnlyTextTest + { + [MatchOnlyText] + public string MatchOnlyText { get; set; } + } + + [SkipVersion("<7.14.0", "Match only text property added in 7.14.0")] + public class MatchOnlyTextAttributeTests : AttributeTestsBase + { + protected override object ExpectJson => new { properties = new { matchOnlyText = new { type = "match_only_text" } } }; + } +} diff --git a/tests/Tests/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextPropertyTests.cs b/tests/Tests/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextPropertyTests.cs new file mode 100644 index 00000000000..bbb10a11c95 --- /dev/null +++ b/tests/Tests/Mapping/Types/Core/MatchOnlyText/MatchOnlyTextPropertyTests.cs @@ -0,0 +1,28 @@ +// 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.Core.MatchOnlyText +{ + [SkipVersion("<7.14.0", "Match only text property added in 7.14.0")] + public class MatchOnlyTextPropertyTests : PropertyTestsBase + { + public MatchOnlyTextPropertyTests(WritableCluster cluster, EndpointUsage usage) : base(cluster, usage) { } + + protected override object ExpectJson => new { properties = new { name = new { type = "match_only_text" } } }; + + protected override Func, IPromise> FluentProperties => f => f + .MatchOnlyText(s => s + .Name(p => p.Name) + ); + + protected override IProperties InitializerProperties => new Properties { { "name", new MatchOnlyTextProperty() } }; + } +}