Skip to content

Commit 91b92b9

Browse files
Mpdreamzrusscam
authored andcommitted
Allow context to be specified on the painless execute API (#3435)
(cherry picked from commit ae5ae5b)
1 parent b6af984 commit 91b92b9

File tree

4 files changed

+174
-3
lines changed

4 files changed

+174
-3
lines changed

src/Nest/Modules/Scripting/ExecutePainlessScript/ExecutePainlessScriptRequest.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,50 @@
33

44
namespace Nest
55
{
6+
/// <summary> The Painless execute API allows an arbitrary script to be executed and a result to be returned. </summary>
67
public partial interface IExecutePainlessScriptRequest
78
{
9+
/// <summary> The script to execute </summary>
810
[JsonProperty("script")]
911
IInlineScript Script { get; set; }
12+
13+
/// <summary> The context the script should be executed in </summary>
14+
[JsonProperty("context")]
15+
string Context { get; set; }
16+
17+
/// <inheritdoc cref="IPainlessContextSetup"/>
18+
[JsonProperty("context_setup")]
19+
IPainlessContextSetup ContextSetup { get; set; }
1020
}
1121

22+
/// <inheritdoc cref="IExecutePainlessScriptRequest"/>
1223
public partial class ExecutePainlessScriptRequest
1324
{
25+
/// <inheritdoc cref="IExecutePainlessScriptRequest.Script"/>
1426
public IInlineScript Script { get; set; }
27+
/// <inheritdoc cref="IExecutePainlessScriptRequest.Context"/>
28+
public string Context { get; set; }
29+
/// <inheritdoc cref="IExecutePainlessScriptRequest.ContextSetup"/>
30+
public IPainlessContextSetup ContextSetup { get; set; }
1531
}
1632

33+
/// <inheritdoc cref="IExecutePainlessScriptRequest"/>
1734
[DescriptorFor("ScriptsPainlessExecute")]
1835
public partial class ExecutePainlessScriptDescriptor
1936
{
2037
IInlineScript IExecutePainlessScriptRequest.Script { get; set; }
38+
string IExecutePainlessScriptRequest.Context { get; set; }
39+
IPainlessContextSetup IExecutePainlessScriptRequest.ContextSetup { get; set; }
2140

41+
/// <inheritdoc cref="IExecutePainlessScriptRequest.Script"/>
2242
public ExecutePainlessScriptDescriptor Script(Func<InlineScriptDescriptor, IInlineScript> selector) =>
2343
Assign(a => a.Script = selector?.Invoke(new InlineScriptDescriptor()));
44+
45+
/// <inheritdoc cref="IExecutePainlessScriptRequest.ContextSetup"/>
46+
public ExecutePainlessScriptDescriptor ContextSetup(Func<PainlessContextSetupDescriptor, IPainlessContextSetup> selector) =>
47+
Assign(a => a.ContextSetup = selector?.Invoke(new PainlessContextSetupDescriptor()));
48+
49+
/// <inheritdoc cref="IExecutePainlessScriptRequest.Context"/>
50+
public ExecutePainlessScriptDescriptor Context(string context) => Assign(a => a.Context = context);
2451
}
2552
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using Newtonsoft.Json;
3+
4+
namespace Nest
5+
{
6+
/// <summary>
7+
/// Sets up contextual scope for the painless script the execute under.
8+
/// </summary>
9+
[JsonConverter(typeof(ReadAsTypeJsonConverter<PainlessContextSetup>))]
10+
public interface IPainlessContextSetup
11+
{
12+
/// <summary>
13+
/// Contains the document that will be temporarily indexed in-memory and is accessible from the script.
14+
/// </summary>
15+
[JsonProperty("document")]
16+
object Document { get; set; }
17+
/// <summary>
18+
/// The name of an index containing a mapping that is compatible with the document being indexed.
19+
/// </summary>
20+
[JsonProperty("index")]
21+
IndexName Index { get; set; }
22+
/// <summary>
23+
/// If _score is used in the script then a query can specified that will be used to compute a score.
24+
/// </summary>
25+
[JsonProperty("query")]
26+
QueryContainer Query { get; set; }
27+
}
28+
29+
/// <inheritdoc cref="IPainlessContextSetup"/>
30+
public class PainlessContextSetup : IPainlessContextSetup
31+
{
32+
/// <inheritdoc cref="IPainlessContextSetup.Document"/>
33+
public object Document { get; set; }
34+
/// <inheritdoc cref="IPainlessContextSetup.Index"/>
35+
public IndexName Index { get; set; }
36+
/// <inheritdoc cref="IPainlessContextSetup.Query"/>
37+
public QueryContainer Query { get; set; }
38+
}
39+
40+
/// <inheritdoc cref="IPainlessContextSetup"/>
41+
public class PainlessContextSetupDescriptor : DescriptorBase<PainlessContextSetupDescriptor, IPainlessContextSetup>, IPainlessContextSetup
42+
{
43+
object IPainlessContextSetup.Document { get; set; }
44+
IndexName IPainlessContextSetup.Index { get; set; }
45+
QueryContainer IPainlessContextSetup.Query { get; set; }
46+
47+
/// <inheritdoc cref="IPainlessContextSetup.Document"/>
48+
public PainlessContextSetupDescriptor Document<T>(T document) => Assign(a => a.Document = document);
49+
/// <inheritdoc cref="IPainlessContextSetup.Index"/>
50+
public PainlessContextSetupDescriptor Index(IndexName index) => Assign(a => a.Index = index);
51+
/// <inheritdoc cref="IPainlessContextSetup.Query"/>
52+
public PainlessContextSetupDescriptor Query<T>(Func<QueryContainerDescriptor<T>, QueryContainer> querySelector) where T : class =>
53+
Assign(a => a.Query = querySelector?.Invoke(new QueryContainerDescriptor<T>()));
54+
}
55+
}

src/Tests/Tests/Framework/EndpointTests/TestState/CallUniqueValues.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@
66

77
namespace Tests.Framework.Integration
88
{
9+
/// <summary>
10+
/// Holds unique values for the the two DSL's and the exposed sync and async methods we expose
11+
/// <see cref="ClientMethod"/>
12+
/// </summary>
913
public class CallUniqueValues : Dictionary<ClientMethod, string>
1014
{
1115
private readonly string _prefix;
1216
private string UniqueValue => $"{this._prefix}-{ViewName}-{Guid.NewGuid().ToString("N").Substring(0, 8)}";
17+
public string FixedForAllCallsValue { get; }
1318

1419
private IDictionary<ClientMethod, ConcurrentDictionary<string, object>> ExtendedValues { get; }
1520
= new Dictionary<ClientMethod, ConcurrentDictionary<string, object>>();
@@ -28,6 +33,7 @@ public T ExtendedValue<T>(string key, Func<T> value) where T : class =>
2833
public CallUniqueValues(string prefix = "nest")
2934
{
3035
this._prefix = prefix;
36+
this.FixedForAllCallsValue = this.UniqueValue;
3137
this.SetupClientMethod(Fluent);
3238
this.SetupClientMethod(FluentAsync);
3339
this.SetupClientMethod(Initializer);

src/Tests/Tests/Modules/Scripting/ExecutePainlessScript/ExecutePainlessScriptApiTests.cs

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Threading.Tasks;
43
using Elastic.Xunit.XunitPlumbing;
54
using Elasticsearch.Net;
65
using FluentAssertions;
@@ -9,8 +8,6 @@
98
using Tests.Core.ManagedElasticsearch.Clusters;
109
using Tests.Framework;
1110
using Tests.Framework.Integration;
12-
using Tests.Framework.ManagedElasticsearch.Clusters;
13-
using Xunit;
1411

1512
namespace Tests.Modules.Scripting.ExecutePainlessScript
1613
{
@@ -69,4 +66,90 @@ protected override void ExpectResponse(IExecutePainlessScriptResponse<string> re
6966
response.Result.Should().NotBeNullOrWhiteSpace();
7067
}
7168
}
69+
70+
[SkipVersion("<6.4.0", "Context only tested on 6.4.0 when they were introduced")]
71+
public class ExecutePainlessScriptContextApiTests
72+
: ApiIntegrationTestBase<WritableCluster, IExecutePainlessScriptResponse<string>, IExecutePainlessScriptRequest, ExecutePainlessScriptDescriptor, ExecutePainlessScriptRequest>
73+
{
74+
public ExecutePainlessScriptContextApiTests(WritableCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
75+
76+
private static readonly string _painlessScript = "doc['rank'].value / params.max_rank";
77+
78+
protected override LazyResponses ClientUsage() => Calls(
79+
fluent: (client, f) => client.ExecutePainlessScript<string>(f),
80+
fluentAsync: (client, f) => client.ExecutePainlessScriptAsync<string>(f),
81+
request: (client, r) => client.ExecutePainlessScript<string>(r),
82+
requestAsync: (client, r) => client.ExecutePainlessScriptAsync<string>(r)
83+
);
84+
85+
protected override HttpMethod HttpMethod => HttpMethod.POST;
86+
protected override string UrlPath => "/_scripts/painless/_execute";
87+
protected override int ExpectStatusCode => 200;
88+
protected override bool ExpectIsValid => true;
89+
protected override bool SupportsDeserialization => false;
90+
91+
private class ScriptDocument
92+
{
93+
public string Field { get; set; }
94+
public long Rank { get; set; }
95+
}
96+
97+
protected override void IntegrationSetup(IElasticClient client, CallUniqueValues values)
98+
{
99+
var create =client.CreateIndex(values.FixedForAllCallsValue, c => c.Mappings(map => map.Map<ScriptDocument>(m => m.AutoMap())));
100+
create.ShouldBeValid();
101+
}
102+
103+
protected override object ExpectJson => new
104+
{
105+
context = "score",
106+
context_setup = new
107+
{
108+
document = new { rank = 4 },
109+
index = this.UniqueValues.FixedForAllCallsValue,
110+
query = new { match_all = new {} }
111+
},
112+
script = new
113+
{
114+
source = _painlessScript,
115+
@params = new { max_rank = 5.0 }
116+
},
117+
};
118+
119+
protected override Func<ExecutePainlessScriptDescriptor, IExecutePainlessScriptRequest> Fluent => d => d
120+
.ContextSetup(cs=>cs
121+
.Index(this.UniqueValues.FixedForAllCallsValue)
122+
.Document(new ScriptDocument { Rank = 4 })
123+
.Query<ScriptDocument>(q=>q.MatchAll())
124+
)
125+
.Context("score")
126+
.Script(s=>s
127+
.Source(_painlessScript)
128+
.Params(p => p.Add("max_rank", 5.0))
129+
);
130+
131+
protected override ExecutePainlessScriptRequest Initializer => new ExecutePainlessScriptRequest
132+
{
133+
ContextSetup = new PainlessContextSetup
134+
{
135+
Index = this.UniqueValues.FixedForAllCallsValue,
136+
Document = new ScriptDocument { Rank = 4 },
137+
Query = new MatchAllQuery()
138+
},
139+
Context = "score",
140+
Script = new InlineScript(_painlessScript)
141+
{
142+
Params = new Dictionary<string, object>
143+
{
144+
{ "max_rank", 5.0 },
145+
}
146+
}
147+
};
148+
149+
protected override void ExpectResponse(IExecutePainlessScriptResponse<string> response)
150+
{
151+
response.ShouldBeValid();
152+
response.Result.Should().NotBeNullOrWhiteSpace();
153+
}
154+
}
72155
}

0 commit comments

Comments
 (0)