Skip to content

Commit

Permalink
Add per-field metadata (#4376)
Browse files Browse the repository at this point in the history
Relates: #4341

This commit adds the ability to define per-field metadata in mappings,
which will be returned via the Get Mapping API and Field Capabilities API.

(cherry picked from commit 9bc7470)
  • Loading branch information
russcam committed Feb 10, 2020
1 parent f29e35e commit 3b7a847
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public abstract class ElasticsearchPropertyAttributeBase : Attribute, IProperty,

IDictionary<string, object> IProperty.LocalMetadata { get; set; }

IDictionary<string, string> IProperty.Meta { get; set; }

PropertyName IProperty.Name { get; set; }
private IProperty Self => this;
string IProperty.Type { get; set; }
Expand Down
17 changes: 16 additions & 1 deletion src/Nest/Mapping/Types/PropertyBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ public interface IProperty : IFieldMapping
[IgnoreDataMember]
IDictionary<string, object> LocalMetadata { get; set; }

/// <summary>
/// Metadata attached to the field. This metadata is stored in but opaque to Elasticsearch. It is
/// only useful for multiple applications that work on the same indices to share
/// meta information about fields such as units.
///<para></para>
/// Field metadata enforces at most 5 entries, that keys have a length that
/// is less than or equal to 20, and that values are strings whose length is less
/// than or equal to 50.
/// </summary>
[DataMember(Name = "meta")]
IDictionary<string, string> Meta { get; set; }

/// <summary>
/// The name of the property
/// </summary>
Expand Down Expand Up @@ -53,11 +65,14 @@ public abstract class PropertyBase : IProperty, IPropertyWithClrOrigin
/// <inheritdoc />
public IDictionary<string, object> LocalMetadata { get; set; }

/// <inheritdoc />
public IDictionary<string, string> Meta { get; set; }

/// <inheritdoc />
public PropertyName Name { get; set; }

protected string DebugDisplay => $"Type: {((IProperty)this).Type ?? "<empty>"}, Name: {Name.DebugDisplay} ";

public override string ToString() => DebugDisplay;

/// <summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Nest/Mapping/Types/PropertyDescriptorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ protected string TypeOverride

IDictionary<string, object> IProperty.LocalMetadata { get; set; }
PropertyName IProperty.Name { get; set; }
IDictionary<string, string> IProperty.Meta { get; set; }

string IProperty.Type
{
Expand All @@ -41,5 +42,9 @@ string IProperty.Type
/// <inheritdoc cref="IProperty.LocalMetadata" />
public TDescriptor LocalMetadata(Func<FluentDictionary<string, object>, FluentDictionary<string, object>> selector) =>
Assign(selector, (a, v) => a.LocalMetadata = v?.Invoke(new FluentDictionary<string, object>()));

/// <inheritdoc cref="IProperty.Meta" />
public TDescriptor Meta(Func<FluentDictionary<string, string>, FluentDictionary<string, string>> selector) =>
Assign(selector, (a, v) => a.Meta = v?.Invoke(new FluentDictionary<string, string>()));
}
}
1 change: 1 addition & 0 deletions src/Nest/Mapping/Types/PropertyFormatter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Elasticsearch.Net.Utf8Json;
using Elasticsearch.Net.Utf8Json.Internal;
using Elasticsearch.Net.Utf8Json.Resolvers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,8 @@ public class FieldCapabilities

[DataMember(Name = "searchable")]
public bool Searchable { get; internal set; }

[DataMember(Name = "meta")]
public IReadOnlyDictionary<string, string[]> Meta { get; internal set; } = EmptyReadOnly<string, string[]>.Dictionary;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class ExtendingNestTypes
public class MyPluginProperty : IProperty
{
IDictionary<string, object> IProperty.LocalMetadata { get; set; }
IDictionary<string, string> IProperty.Meta { get; set; }
public string Type { get; set; } = "my_plugin_property";
public PropertyName Name { get; set; }

Expand Down
11 changes: 6 additions & 5 deletions tests/Tests/Mapping/LocalMetadata/LocalMetadataVisitorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
using Nest;
using Tests.Mapping.Types.Core.Text;

namespace Tests.Mapping.LocalMetadata {
namespace Tests.Mapping.LocalMetadata
{
public class LocalMetadataVisitorTests
{
[U]
Expand All @@ -17,7 +18,7 @@ public void CanAssignAndAccessLocalMetadataInitializer()
.AddTestLocalMetadata()
)) as ITypeMapping;

var visitor = new LocalMatadataVisitor();
var visitor = new LocalMetadataVisitor();
var walker = new MappingWalker(visitor);
walker.Accept(descriptor.Properties);

Expand All @@ -36,7 +37,7 @@ public void CanAssignAndAccessLocalMetadataFluent()
)
)) as ITypeMapping;

var visitor = new LocalMatadataVisitor();
var visitor = new LocalMetadataVisitor();
var walker = new MappingWalker(visitor);
walker.Accept(descriptor.Properties);

Expand All @@ -63,7 +64,7 @@ public static TDescriptor AddTestLocalMetadata<TDescriptor>(this TDescriptor des
}
}

public class LocalMatadataVisitor : NoopMappingVisitor
public class LocalMetadataVisitor : NoopMappingVisitor
{
public int MetadataCount { get; set; }

Expand All @@ -77,4 +78,4 @@ public override void Visit(ITextProperty property)
property.LocalMetadata.Should().Contain("Test", "TestValue");
}
}
}
}
62 changes: 62 additions & 0 deletions tests/Tests/Mapping/Meta/MetaMappingApiTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using Elastic.Xunit.XunitPlumbing;
using FluentAssertions;
using Nest;
using Tests.ClientConcepts.HighLevel.CovariantHits;
using Tests.Core.Extensions;
using Tests.Core.ManagedElasticsearch.Clusters;
using Tests.Domain;
using Tests.Framework.EndpointTests.TestState;
using Tests.Mapping.Types;

namespace Tests.Mapping.Meta
{
[SkipVersion("<7.6.0", "Meta added in Elasticsearch 7.6.0")]
public class MetaMappingApiTests
: PropertyTestsBase
{
public MetaMappingApiTests(WritableCluster cluster, EndpointUsage usage) : base(cluster, usage) { }

protected override Func<PropertiesDescriptor<Project>, IPromise<IProperties>> FluentProperties => p => p
.Number(n => n
.Name(nn => nn.Rank)
.Type(NumberType.Integer)
.Meta(m => m
.Add("unit", "popularity")
)
);
protected override IProperties InitializerProperties => new Properties<Project>
{
{ n => n.Rank, new NumberProperty(NumberType.Integer)
{
Meta = new Dictionary<string, string>
{
{ "unit", "popularity" }
}
}
}
};

protected override void ExpectResponse(PutMappingResponse response)
{
base.ExpectResponse(response);

// check the meta shows up in get mapping API
var getMappingResponse = Client.Indices.GetMapping<Project>(m => m.Index(CallIsolatedValue));
getMappingResponse.IsValid.Should().BeTrue();
var mappingMeta = getMappingResponse.Indices[CallIsolatedValue].Mappings.Properties["rank"].Meta;
mappingMeta.Should().NotBeNull().And.ContainKey("unit");
mappingMeta["unit"].Should().Be("popularity");

// check the meta shows up in field capabilities API
var fieldCapsResponse = Client.FieldCapabilities(CallIsolatedValue, f => f
.Fields<Project>(ff => ff.Rank)
);
fieldCapsResponse.IsValid.Should().BeTrue();
var meta = fieldCapsResponse.Fields["rank"].Integer.Meta;
meta.Should().NotBeNull().And.ContainKey("unit");
meta["unit"].Should().BeEquivalentTo("popularity");
}
}
}
25 changes: 0 additions & 25 deletions tests/Tests/Mapping/Metafields/MetafieldsMappingApiTestsBase.cs

This file was deleted.

0 comments on commit 3b7a847

Please sign in to comment.