Skip to content

[Backport master] Support mapping runtime fields #5234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ public partial class PutMappingRequest
/// <inheritdoc />
public IRoutingField RoutingField { get; set; }

/// <inheritdoc />
public IRuntimeFields RuntimeFields { get; set; }

/// <inheritdoc />
public ISizeField SizeField { get; set; }

Expand All @@ -82,6 +85,7 @@ public partial class PutMappingDescriptor<TDocument> where TDocument : class
bool? ITypeMapping.NumericDetection { get; set; }
IProperties ITypeMapping.Properties { get; set; }
IRoutingField ITypeMapping.RoutingField { get; set; }
IRuntimeFields ITypeMapping.RuntimeFields { get; set; }
ISizeField ITypeMapping.SizeField { get; set; }
ISourceField ITypeMapping.SourceField { get; set; }

Expand Down Expand Up @@ -150,6 +154,10 @@ public PutMappingDescriptor<TDocument> SourceField(Func<SourceFieldDescriptor, I
public PutMappingDescriptor<TDocument> RoutingField(Func<RoutingFieldDescriptor<TDocument>, IRoutingField> routingFieldSelector) =>
Assign(routingFieldSelector, (a, v) => a.RoutingField = v?.Invoke(new RoutingFieldDescriptor<TDocument>()));

/// <inheritdoc cref="ITypeMapping.RuntimeFields" />
public PutMappingDescriptor<TDocument> RuntimeFields(Func<RuntimeFieldsDescriptor, IPromise<IRuntimeFields>> runtimeFieldsSelector) =>
Assign(runtimeFieldsSelector, (a, v) => a.RuntimeFields = v?.Invoke(new RuntimeFieldsDescriptor())?.Value);

/// <inheritdoc cref="ITypeMapping.FieldNamesField" />
public PutMappingDescriptor<TDocument> FieldNamesField(Func<FieldNamesFieldDescriptor<TDocument>, IFieldNamesField> fieldNamesFieldSelector) =>
Assign(fieldNamesFieldSelector, (a, v) => a.FieldNamesField = v.Invoke(new FieldNamesFieldDescriptor<TDocument>()));
Expand Down
2 changes: 2 additions & 0 deletions src/Nest/Mapping/Mappings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ public abstract class ObsoleteMappingsBase : ITypeMapping
IProperties ITypeMapping.Properties { get => Wrapped.Properties; set => Wrapped.Properties = value; }
[DataMember(Name = "_routing")]
IRoutingField ITypeMapping.RoutingField { get => Wrapped.RoutingField; set => Wrapped.RoutingField = value; }
[DataMember(Name = "runtime")]
IRuntimeFields ITypeMapping.RuntimeFields { get => Wrapped.RuntimeFields; set => Wrapped.RuntimeFields = value; }
[DataMember(Name = "_size")]
ISizeField ITypeMapping.SizeField { get => Wrapped.SizeField; set => Wrapped.SizeField = value; }
[DataMember(Name = "_source")]
Expand Down
59 changes: 59 additions & 0 deletions src/Nest/Mapping/RuntimeFields/RuntimeField.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// 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.Runtime.Serialization;
using Nest.Utf8Json;

namespace Nest
{
[InterfaceDataContract]
[ReadAs(typeof(RuntimeField))]
public interface IRuntimeField
{
/// <summary>
/// Runtime fields with a type of date can accept the format parameter exactly as the date field type.
/// <see cref="DateFormat" />
/// </summary>
[DataMember(Name = "format")]
string Format { get; set; }

/// <summary>
/// The script to be evaluated for field calculation at search time.
/// </summary>
[DataMember(Name = "script")]
IStoredScript Script { get; set; }

/// <summary>
/// The datatype of the runtime field.
/// </summary>
[DataMember(Name = "type")]
FieldType Type { get; set; }
}

public class RuntimeField : IRuntimeField
{
/// <inheritdoc />
public string Format { get; set; }
/// <inheritdoc />
public IStoredScript Script { get; set; }
/// <inheritdoc />
public FieldType Type { get; set; }
}

public class RuntimeFieldDescriptor
: DescriptorBase<RuntimeFieldDescriptor, IRuntimeField>, IRuntimeField
{
public RuntimeFieldDescriptor(FieldType fieldType) => Self.Type = fieldType;

string IRuntimeField.Format { get; set; }
IStoredScript IRuntimeField.Script { get; set; }
FieldType IRuntimeField.Type { get; set; }

public RuntimeFieldDescriptor Format(string format) => Assign(format, (a, v) => a.Format = v);

public RuntimeFieldDescriptor Script(IStoredScript script) => Assign(script, (a, v) => a.Script = v);

public RuntimeFieldDescriptor Script(string source) => Assign(source, (a, v) => a.Script = new PainlessScript(source));
}
}
36 changes: 36 additions & 0 deletions src/Nest/Mapping/RuntimeFields/RuntimeFields.cs
Original file line number Diff line number Diff line change
@@ -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;
using System.Collections.Generic;
using Nest.Utf8Json;

namespace Nest
{
[JsonFormatter(typeof(VerbatimDictionaryKeysFormatter<RuntimeFields, IRuntimeFields, string, IRuntimeField>))]
public interface IRuntimeFields : IIsADictionary<string, IRuntimeField> { }

public class RuntimeFields : IsADictionaryBase<string, IRuntimeField>, IRuntimeFields
{
public RuntimeFields() { }

public RuntimeFields(IDictionary<string, IRuntimeField> container) : base(container) { }

public RuntimeFields(Dictionary<string, IRuntimeField> container) : base(container) { }

public void Add(string name, IRuntimeField runtimeField) => BackingDictionary.Add(name, runtimeField);
}

public class RuntimeFieldsDescriptor
: IsADictionaryDescriptorBase<RuntimeFieldsDescriptor, RuntimeFields, string, IRuntimeField>
{
public RuntimeFieldsDescriptor() : base(new RuntimeFields()) { }

public RuntimeFieldsDescriptor RuntimeField(string name, FieldType type, Func<RuntimeFieldDescriptor, IRuntimeField> selector) =>
Assign(name, selector?.Invoke(new RuntimeFieldDescriptor(type)));

public RuntimeFieldsDescriptor RuntimeField(string name, FieldType type) =>
Assign(name, new RuntimeFieldDescriptor(type));
}
}
14 changes: 14 additions & 0 deletions src/Nest/Mapping/TypeMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ public interface ITypeMapping
[DataMember(Name = "_routing")]
IRoutingField RoutingField { get; set; }

/// <summary>
/// Specifies runtime fields for the mapping.
/// </summary>
[DataMember(Name = "runtime")]
IRuntimeFields RuntimeFields { get; set; }

/// <summary>
/// If enabled, indexes the size in bytes of the original _source field.
/// Requires mapper-size plugin be installed
Expand Down Expand Up @@ -147,6 +153,9 @@ public class TypeMapping : ITypeMapping
/// <inheritdoc />
public IRoutingField RoutingField { get; set; }

/// <inheritdoc />
public IRuntimeFields RuntimeFields { get; set; }

/// <inheritdoc />
public ISizeField SizeField { get; set; }

Expand All @@ -171,6 +180,7 @@ public class TypeMappingDescriptor<T> : DescriptorBase<TypeMappingDescriptor<T>,
bool? ITypeMapping.NumericDetection { get; set; }
IProperties ITypeMapping.Properties { get; set; }
IRoutingField ITypeMapping.RoutingField { get; set; }
IRuntimeFields ITypeMapping.RuntimeFields { get; set; }
ISizeField ITypeMapping.SizeField { get; set; }
ISourceField ITypeMapping.SourceField { get; set; }

Expand Down Expand Up @@ -259,6 +269,10 @@ public TypeMappingDescriptor<T> DisableIndexField(bool? disabled = true) =>
public TypeMappingDescriptor<T> RoutingField(Func<RoutingFieldDescriptor<T>, IRoutingField> routingFieldSelector) =>
Assign(routingFieldSelector, (a, v) => a.RoutingField = v?.Invoke(new RoutingFieldDescriptor<T>()));

/// <inheritdoc cref="ITypeMapping.RuntimeFields" />
public TypeMappingDescriptor<T> RuntimeFields(Func<RuntimeFieldsDescriptor, IPromise<IRuntimeFields>> runtimeFieldsSelector) =>
Assign(runtimeFieldsSelector, (a, v) => a.RuntimeFields = v?.Invoke(new RuntimeFieldsDescriptor())?.Value);

/// <inheritdoc cref="ITypeMapping.FieldNamesField" />
public TypeMappingDescriptor<T> FieldNamesField(Func<FieldNamesFieldDescriptor<T>, IFieldNamesField> fieldNamesFieldSelector) =>
Assign(fieldNamesFieldSelector.Invoke(new FieldNamesFieldDescriptor<T>()), (a, v) => a.FieldNamesField = v);
Expand Down
3 changes: 3 additions & 0 deletions src/Nest/Search/Search/SearchRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ public partial interface ISearchRequest : ITypedSearchRequest
[DataMember(Name = "version")]
bool? Version { get; set; }

/// <summary>
/// The <see cref="PointInTime"/> to search over.
/// </summary>
[DataMember(Name = "pit")]
IPointInTime PointInTime { get; set; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,4 +338,61 @@ protected override LazyResponses ClientUsage() => Calls(
(client, r) => client.MapAsync(r)
);
}

[SkipVersion("<7.11.0", "Runtime fields introduced in 7.11.0")]
public class PutMappingWithRuntimeFieldsTests : ApiTestBase<ReadOnlyCluster, PutMappingResponse, IPutMappingRequest, PutMappingDescriptor<Project>, PutMappingRequest>
{
// These test serialisation only. Integration tests take place in RuntimeFieldsTests.cs

private const string ScriptValue = "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT))";

public PutMappingWithRuntimeFieldsTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { }

protected override HttpMethod HttpMethod => HttpMethod.PUT;

protected override string UrlPath => $"/{CallIsolatedValue}/_mapping";

protected override PutMappingRequest Initializer => new(CallIsolatedValue)
{
RuntimeFields = new RuntimeFields
{
{ "runtime_date", new RuntimeField { Type = FieldType.Date, Format = "yyyy-MM-dd" } },
{ "runtime_scripted", new RuntimeField { Type = FieldType.Keyword, Script = new PainlessScript(ScriptValue) } }
}
};

protected override Func<PutMappingDescriptor<Project>, IPutMappingRequest> Fluent => d => d
.Index(CallIsolatedValue)
.RuntimeFields(rtf => rtf
.RuntimeField("runtime_date", FieldType.Date, rf => rf.Format("yyyy-MM-dd"))
.RuntimeField("runtime_scripted", FieldType.Keyword, rf=> rf.Script(new PainlessScript(ScriptValue))));

protected override LazyResponses ClientUsage() => Calls(
(client, f) => client.Indices.PutMapping(f),
(client, f) => client.Indices.PutMappingAsync(f),
(client, r) => client.Indices.PutMapping(r),
(client, r) => client.Indices.PutMappingAsync(r)
);

protected override object ExpectJson => new
{
runtime = new
{
runtime_date = new
{
type = "date",
format = "yyyy-MM-dd"
},
runtime_scripted = new
{
type = "keyword",
script = new
{
lang = "painless",
source = ScriptValue
}
}
}
};
}
}
Loading