Skip to content

Commit 8c7bf80

Browse files
stevejgordongithub-actions[bot]
authored andcommitted
Support mapping runtime fields (#5230)
* Add support for mapping runtime fields * Add missing XML comment for PIT
1 parent daa9b77 commit 8c7bf80

File tree

8 files changed

+359
-0
lines changed

8 files changed

+359
-0
lines changed

src/Nest/Indices/MappingManagement/PutMapping/PutMappingRequest.cs

+8
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public partial class PutMappingRequest
5656
/// <inheritdoc />
5757
public IRoutingField RoutingField { get; set; }
5858

59+
/// <inheritdoc />
60+
public IRuntimeFields RuntimeFields { get; set; }
61+
5962
/// <inheritdoc />
6063
public ISizeField SizeField { get; set; }
6164

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

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

157+
/// <inheritdoc cref="ITypeMapping.RuntimeFields" />
158+
public PutMappingDescriptor<TDocument> RuntimeFields(Func<RuntimeFieldsDescriptor, IPromise<IRuntimeFields>> runtimeFieldsSelector) =>
159+
Assign(runtimeFieldsSelector, (a, v) => a.RuntimeFields = v?.Invoke(new RuntimeFieldsDescriptor())?.Value);
160+
153161
/// <inheritdoc cref="ITypeMapping.FieldNamesField" />
154162
public PutMappingDescriptor<TDocument> FieldNamesField(Func<FieldNamesFieldDescriptor<TDocument>, IFieldNamesField> fieldNamesFieldSelector) =>
155163
Assign(fieldNamesFieldSelector, (a, v) => a.FieldNamesField = v.Invoke(new FieldNamesFieldDescriptor<TDocument>()));

src/Nest/Mapping/Mappings.cs

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ public abstract class ObsoleteMappingsBase : ITypeMapping
5757
IProperties ITypeMapping.Properties { get => Wrapped.Properties; set => Wrapped.Properties = value; }
5858
[DataMember(Name = "_routing")]
5959
IRoutingField ITypeMapping.RoutingField { get => Wrapped.RoutingField; set => Wrapped.RoutingField = value; }
60+
[DataMember(Name = "runtime")]
61+
IRuntimeFields ITypeMapping.RuntimeFields { get => Wrapped.RuntimeFields; set => Wrapped.RuntimeFields = value; }
6062
[DataMember(Name = "_size")]
6163
ISizeField ITypeMapping.SizeField { get => Wrapped.SizeField; set => Wrapped.SizeField = value; }
6264
[DataMember(Name = "_source")]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using System.Runtime.Serialization;
6+
using Elasticsearch.Net.Utf8Json;
7+
8+
namespace Nest
9+
{
10+
[InterfaceDataContract]
11+
[ReadAs(typeof(RuntimeField))]
12+
public interface IRuntimeField
13+
{
14+
/// <summary>
15+
/// Runtime fields with a type of date can accept the format parameter exactly as the date field type.
16+
/// <see cref="DateFormat" />
17+
/// </summary>
18+
[DataMember(Name = "format")]
19+
string Format { get; set; }
20+
21+
/// <summary>
22+
/// The script to be evaluated for field calculation at search time.
23+
/// </summary>
24+
[DataMember(Name = "script")]
25+
IStoredScript Script { get; set; }
26+
27+
/// <summary>
28+
/// The datatype of the runtime field.
29+
/// </summary>
30+
[DataMember(Name = "type")]
31+
FieldType Type { get; set; }
32+
}
33+
34+
public class RuntimeField : IRuntimeField
35+
{
36+
/// <inheritdoc />
37+
public string Format { get; set; }
38+
/// <inheritdoc />
39+
public IStoredScript Script { get; set; }
40+
/// <inheritdoc />
41+
public FieldType Type { get; set; }
42+
}
43+
44+
public class RuntimeFieldDescriptor
45+
: DescriptorBase<RuntimeFieldDescriptor, IRuntimeField>, IRuntimeField
46+
{
47+
public RuntimeFieldDescriptor(FieldType fieldType) => Self.Type = fieldType;
48+
49+
string IRuntimeField.Format { get; set; }
50+
IStoredScript IRuntimeField.Script { get; set; }
51+
FieldType IRuntimeField.Type { get; set; }
52+
53+
public RuntimeFieldDescriptor Format(string format) => Assign(format, (a, v) => a.Format = v);
54+
55+
public RuntimeFieldDescriptor Script(IStoredScript script) => Assign(script, (a, v) => a.Script = v);
56+
57+
public RuntimeFieldDescriptor Script(string source) => Assign(source, (a, v) => a.Script = new PainlessScript(source));
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using Elasticsearch.Net.Utf8Json;
8+
9+
namespace Nest
10+
{
11+
[JsonFormatter(typeof(VerbatimDictionaryKeysFormatter<RuntimeFields, IRuntimeFields, string, IRuntimeField>))]
12+
public interface IRuntimeFields : IIsADictionary<string, IRuntimeField> { }
13+
14+
public class RuntimeFields : IsADictionaryBase<string, IRuntimeField>, IRuntimeFields
15+
{
16+
public RuntimeFields() { }
17+
18+
public RuntimeFields(IDictionary<string, IRuntimeField> container) : base(container) { }
19+
20+
public RuntimeFields(Dictionary<string, IRuntimeField> container) : base(container) { }
21+
22+
public void Add(string name, IRuntimeField runtimeField) => BackingDictionary.Add(name, runtimeField);
23+
}
24+
25+
public class RuntimeFieldsDescriptor
26+
: IsADictionaryDescriptorBase<RuntimeFieldsDescriptor, RuntimeFields, string, IRuntimeField>
27+
{
28+
public RuntimeFieldsDescriptor() : base(new RuntimeFields()) { }
29+
30+
public RuntimeFieldsDescriptor RuntimeField(string name, FieldType type, Func<RuntimeFieldDescriptor, IRuntimeField> selector) =>
31+
Assign(name, selector?.Invoke(new RuntimeFieldDescriptor(type)));
32+
33+
public RuntimeFieldsDescriptor RuntimeField(string name, FieldType type) =>
34+
Assign(name, new RuntimeFieldDescriptor(type));
35+
}
36+
}

src/Nest/Mapping/TypeMapping.cs

+14
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ public interface ITypeMapping
9696
[DataMember(Name = "_routing")]
9797
IRoutingField RoutingField { get; set; }
9898

99+
/// <summary>
100+
/// Specifies runtime fields for the mapping.
101+
/// </summary>
102+
[DataMember(Name = "runtime")]
103+
IRuntimeFields RuntimeFields { get; set; }
104+
99105
/// <summary>
100106
/// If enabled, indexes the size in bytes of the original _source field.
101107
/// Requires mapper-size plugin be installed
@@ -147,6 +153,9 @@ public class TypeMapping : ITypeMapping
147153
/// <inheritdoc />
148154
public IRoutingField RoutingField { get; set; }
149155

156+
/// <inheritdoc />
157+
public IRuntimeFields RuntimeFields { get; set; }
158+
150159
/// <inheritdoc />
151160
public ISizeField SizeField { get; set; }
152161

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

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

272+
/// <inheritdoc cref="ITypeMapping.RuntimeFields" />
273+
public TypeMappingDescriptor<T> RuntimeFields(Func<RuntimeFieldsDescriptor, IPromise<IRuntimeFields>> runtimeFieldsSelector) =>
274+
Assign(runtimeFieldsSelector, (a, v) => a.RuntimeFields = v?.Invoke(new RuntimeFieldsDescriptor())?.Value);
275+
262276
/// <inheritdoc cref="ITypeMapping.FieldNamesField" />
263277
public TypeMappingDescriptor<T> FieldNamesField(Func<FieldNamesFieldDescriptor<T>, IFieldNamesField> fieldNamesFieldSelector) =>
264278
Assign(fieldNamesFieldSelector.Invoke(new FieldNamesFieldDescriptor<T>()), (a, v) => a.FieldNamesField = v);

src/Nest/Search/Search/SearchRequest.cs

+3
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ public partial interface ISearchRequest : ITypedSearchRequest
168168
[DataMember(Name = "version")]
169169
bool? Version { get; set; }
170170

171+
/// <summary>
172+
/// The <see cref="PointInTime"/> to search over.
173+
/// </summary>
171174
[DataMember(Name = "pit")]
172175
IPointInTime PointInTime { get; set; }
173176
}

tests/Tests/Indices/MappingManagement/PutMapping/PutMappingApiTest.cs

+57
Original file line numberDiff line numberDiff line change
@@ -338,4 +338,61 @@ protected override LazyResponses ClientUsage() => Calls(
338338
(client, r) => client.MapAsync(r)
339339
);
340340
}
341+
342+
[SkipVersion("<7.11.0", "Runtime fields introduced in 7.11.0")]
343+
public class PutMappingWithRuntimeFieldsTests : ApiTestBase<ReadOnlyCluster, PutMappingResponse, IPutMappingRequest, PutMappingDescriptor<Project>, PutMappingRequest>
344+
{
345+
// These test serialisation only. Integration tests take place in RuntimeFieldsTests.cs
346+
347+
private const string ScriptValue = "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT))";
348+
349+
public PutMappingWithRuntimeFieldsTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
350+
351+
protected override HttpMethod HttpMethod => HttpMethod.PUT;
352+
353+
protected override string UrlPath => $"/{CallIsolatedValue}/_mapping";
354+
355+
protected override PutMappingRequest Initializer => new(CallIsolatedValue)
356+
{
357+
RuntimeFields = new RuntimeFields
358+
{
359+
{ "runtime_date", new RuntimeField { Type = FieldType.Date, Format = "yyyy-MM-dd" } },
360+
{ "runtime_scripted", new RuntimeField { Type = FieldType.Keyword, Script = new PainlessScript(ScriptValue) } }
361+
}
362+
};
363+
364+
protected override Func<PutMappingDescriptor<Project>, IPutMappingRequest> Fluent => d => d
365+
.Index(CallIsolatedValue)
366+
.RuntimeFields(rtf => rtf
367+
.RuntimeField("runtime_date", FieldType.Date, rf => rf.Format("yyyy-MM-dd"))
368+
.RuntimeField("runtime_scripted", FieldType.Keyword, rf=> rf.Script(new PainlessScript(ScriptValue))));
369+
370+
protected override LazyResponses ClientUsage() => Calls(
371+
(client, f) => client.Indices.PutMapping(f),
372+
(client, f) => client.Indices.PutMappingAsync(f),
373+
(client, r) => client.Indices.PutMapping(r),
374+
(client, r) => client.Indices.PutMappingAsync(r)
375+
);
376+
377+
protected override object ExpectJson => new
378+
{
379+
runtime = new
380+
{
381+
runtime_date = new
382+
{
383+
type = "date",
384+
format = "yyyy-MM-dd"
385+
},
386+
runtime_scripted = new
387+
{
388+
type = "keyword",
389+
script = new
390+
{
391+
lang = "painless",
392+
source = ScriptValue
393+
}
394+
}
395+
}
396+
};
397+
}
341398
}

0 commit comments

Comments
 (0)