Skip to content

Support for calculated numeric fields #5622

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 1 commit into from
Apr 23, 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
18 changes: 18 additions & 0 deletions src/Nest/CommonOptions/Scripting/OnScriptError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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 Elasticsearch.Net;

namespace Nest
{
[StringEnum]
public enum OnScriptError
{
[EnumMember(Value = "fail")]
Fail,
[EnumMember(Value = "continue")]
Continue
}
}
16 changes: 15 additions & 1 deletion src/Nest/Mapping/Types/Core/Number/NumberAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,28 @@ public double ScalingFactor
set => Self.ScalingFactor = value;
}

public IInlineScript Script
{
get => Self.Script;
set => Self.Script = value;
}

public OnScriptError OnScriptError
{
get => Self.OnScriptError.GetValueOrDefault();
set => Self.OnScriptError = value;
}

double? INumberProperty.Boost { get; set; }
bool? INumberProperty.Coerce { get; set; }
INumericFielddata INumberProperty.Fielddata { get; set; }
bool? INumberProperty.IgnoreMalformed { get; set; }

bool? INumberProperty.Index { get; set; }
double? INumberProperty.NullValue { get; set; }
double? INumberProperty.ScalingFactor { get; set; }
IInlineScript INumberProperty.Script { get; set; }
OnScriptError? INumberProperty.OnScriptError { get; set; }

private INumberProperty Self => this;
}
}
38 changes: 36 additions & 2 deletions src/Nest/Mapping/Types/Core/Number/NumberProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ public interface INumberProperty : IDocValuesProperty

[DataMember(Name = "scaling_factor")]
double? ScalingFactor { get; set; }

/// <summary>
/// If this parameter is set, then the field will index values generated by this script, rather than reading the values directly from the source.
/// If a value is set for this field on the input document, then the document will be rejected with an error.
/// Scripts are in the same format as their runtime equivalent. Scripts can only be configured on long and double field types.
/// </summary>
[DataMember(Name = "script")]
IInlineScript Script { get; set; }

/// <summary>
/// Defines what to do if the script defined by the `script` parameter throws an error at indexing time.Accepts `reject` (default), which
/// will cause the entire document to be rejected, and `ignore`, which will register the field in the document's ignored metadata field and
/// continue indexing.This parameter can only be set if the `script` field is also set.
/// </summary>
[DataMember(Name = "on_script_error")]
OnScriptError? OnScriptError { get; set; }
}

[DebuggerDisplay("{DebugDisplay}")]
Expand All @@ -50,10 +66,15 @@ public NumberProperty(NumberType type) : base(type.ToFieldType()) { }
public bool? Coerce { get; set; }
public INumericFielddata Fielddata { get; set; }
public bool? IgnoreMalformed { get; set; }

public bool? Index { get; set; }
public double? NullValue { get; set; }
public double? ScalingFactor { get; set; }

/// <inheritdoc />
public IInlineScript Script { get; set; }

/// <inheritdoc />
public OnScriptError? OnScriptError { get; set; }
}

[DebuggerDisplay("{DebugDisplay}")]
Expand All @@ -71,10 +92,11 @@ protected NumberPropertyDescriptorBase(FieldType type) : base(type) { }
bool? INumberProperty.Coerce { get; set; }
INumericFielddata INumberProperty.Fielddata { get; set; }
bool? INumberProperty.IgnoreMalformed { get; set; }

bool? INumberProperty.Index { get; set; }
double? INumberProperty.NullValue { get; set; }
double? INumberProperty.ScalingFactor { get; set; }
IInlineScript INumberProperty.Script { get; set; }
OnScriptError? INumberProperty.OnScriptError { get; set; }

public TDescriptor Type(NumberType? type) => Assign(type?.GetStringValue(), (a, v) => a.Type = v);

Expand All @@ -93,6 +115,18 @@ public TDescriptor Fielddata(Func<NumericFielddataDescriptor, INumericFielddata>
Assign(selector(new NumericFielddataDescriptor()), (a, v) => a.Fielddata = v);

public TDescriptor ScalingFactor(double? scalingFactor) => Assign(scalingFactor, (a, v) => a.ScalingFactor = v);

/// <inheritdoc cref="INumberProperty.Script" />
public TDescriptor Script(IInlineScript inlineScript) => Assign(inlineScript, (a, v) => a.Script = v);

/// <inheritdoc cref="INumberProperty.Script" />
public TDescriptor Script(string source) => Assign(source, (a, v) => a.Script = new InlineScript(source));

/// <inheritdoc cref="INumberProperty.Script" />
public TDescriptor Script(Func<InlineScriptDescriptor, IInlineScript> selector) => Assign(selector, (a, v) => a.Script = v?.Invoke(new InlineScriptDescriptor()));

/// <inheritdoc cref="INumberProperty.OnScriptError" />
public TDescriptor OnScriptError(OnScriptError? onScriptError) => Assign(onScriptError, (a, v) => a.OnScriptError = v);
}

public class NumberPropertyDescriptor<T> : NumberPropertyDescriptorBase<NumberPropertyDescriptor<T>, INumberProperty, T>
Expand Down
52 changes: 52 additions & 0 deletions tests/Tests/Mapping/Types/Core/Number/NumberPropertyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information

using System;
using System.Collections.Generic;
using Elastic.Elasticsearch.Xunit.XunitPlumbing;
using Nest;
using Tests.Core.ManagedElasticsearch.Clusters;
Expand Down Expand Up @@ -166,4 +167,55 @@ public UnsignedLongNumberPropertyTests(WritableCluster cluster, EndpointUsage us
}
};
}

[SkipVersion("<7.13.0", "Script support added in 7.13.0")]
public class ScriptedNumberPropertyTests : PropertyTestsBase
{
public ScriptedNumberPropertyTests(WritableCluster cluster, EndpointUsage usage) : base(cluster, usage) { }

protected override object ExpectJson => new
{
properties = new
{
doublesCommits = new
{
type = "long",
script = new
{
source = "emit((long)(doc['numberOfCommits'].value * params.multiplier))",
@params = new Dictionary<string, int>
{
{"multiplier", 2 }
}
},
on_script_error = "continue"
}
}
};

protected override Func<PropertiesDescriptor<Project>, IPromise<IProperties>> FluentProperties => f => f
.Number(n => n
.Name("doublesCommits")
.Type(NumberType.Long)
.Script(s => s.Source("emit((long)(doc['numberOfCommits'].value * params.multiplier))").Params(p => p.Add("multiplier", 2)))
.OnScriptError(OnScriptError.Continue)
);

protected override IProperties InitializerProperties => new Properties
{
{
"doublesCommits", new NumberProperty(NumberType.Long)
{
Script = new InlineScript("emit((long)(doc['numberOfCommits'].value * params.multiplier))")
{
Params = new Dictionary<string, object>
{
{ "multiplier", 2 }
}
},
OnScriptError = OnScriptError.Continue
}
}
};
}
}