Skip to content

Commit 1500bbd

Browse files
Support runtime mappings for SQL (#5624) (#5631)
Co-authored-by: Steve Gordon <sgordon@hotmail.co.uk>
1 parent e6fde46 commit 1500bbd

File tree

5 files changed

+118
-7
lines changed

5 files changed

+118
-7
lines changed

src/Nest/Mapping/RuntimeFields/RuntimeFields.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,16 @@ public RuntimeFieldsDescriptor<TDocument> RuntimeField(string name, FieldType ty
4040
public RuntimeFieldsDescriptor<TDocument> RuntimeField(Expression<Func<TDocument, Field>> field, FieldType type) =>
4141
Assign(field, new RuntimeFieldDescriptor(type));
4242
}
43+
44+
public class RuntimeFieldsDescriptor
45+
: IsADictionaryDescriptorBase<RuntimeFieldsDescriptor, RuntimeFields, Field, IRuntimeField>
46+
{
47+
public RuntimeFieldsDescriptor() : base(new RuntimeFields()) { }
48+
49+
public RuntimeFieldsDescriptor RuntimeField(string name, FieldType type, Func<RuntimeFieldDescriptor, IRuntimeField> selector) =>
50+
Assign(name, selector?.Invoke(new RuntimeFieldDescriptor(type)));
51+
52+
public RuntimeFieldsDescriptor RuntimeField(string name, FieldType type) =>
53+
Assign(name, new RuntimeFieldDescriptor(type));
54+
}
4355
}

src/Nest/XPack/Sql/ISqlRequest.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,11 @@ public interface ISqlRequest
2929
/// </summary>
3030
[DataMember(Name ="time_zone")]
3131
string TimeZone { get; set; }
32+
33+
/// <summary>
34+
/// Specifies runtime fields which exist only as part of the query.
35+
/// </summary>
36+
[DataMember(Name = "runtime_mappings")]
37+
IRuntimeFields RuntimeFields { get; set; }
3238
}
3339
}

src/Nest/XPack/Sql/QuerySql/QuerySqlRequest.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ public partial class QuerySqlRequest
5555
/// <inheritdoc cref="ISqlRequest.TimeZone" />
5656
/// >
5757
public string TimeZone { get; set; }
58+
59+
/// <inheritdoc />
60+
public IRuntimeFields RuntimeFields { get; set; }
5861
}
5962

6063
public partial class QuerySqlDescriptor
@@ -65,6 +68,7 @@ public partial class QuerySqlDescriptor
6568
QueryContainer ISqlRequest.Filter { get; set; }
6669
string ISqlRequest.Query { get; set; }
6770
string ISqlRequest.TimeZone { get; set; }
71+
IRuntimeFields ISqlRequest.RuntimeFields { get; set; }
6872

6973
/// <inheritdoc cref="ISqlRequest.Query" />
7074
/// >
@@ -90,5 +94,9 @@ public QuerySqlDescriptor Filter<T>(Func<QueryContainerDescriptor<T>, QueryConta
9094
/// <inheritdoc cref="IQuerySqlRequest.Columnar" />
9195
/// >
9296
public QuerySqlDescriptor Columnar(bool? columnar = true) => Assign(columnar, (a, v) => a.Columnar = v);
97+
98+
/// <inheritdoc cref="ISqlRequest.RuntimeFields" />
99+
public QuerySqlDescriptor RuntimeFields(Func<RuntimeFieldsDescriptor, IPromise<IRuntimeFields>> runtimeFieldsSelector) =>
100+
Assign(runtimeFieldsSelector, (a, v) => a.RuntimeFields = v?.Invoke(new RuntimeFieldsDescriptor())?.Value);
93101
}
94102
}

src/Nest/XPack/Sql/TranslateSql/TranslateSqlRequest.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,19 @@ protected sealed override void RequestDefaults(TranslateSqlRequestParameters par
1717
parameters.CustomResponseBuilder = TranslateSqlResponseBuilder.Instance;
1818

1919
/// <inheritdoc cref="ISqlRequest.FetchSize" />
20-
/// >
2120
public int? FetchSize { get; set; }
2221

2322
/// <inheritdoc cref="ISqlRequest.Filter" />
24-
/// >
2523
public QueryContainer Filter { get; set; }
2624

2725
/// <inheritdoc cref="ISqlRequest.Query" />
28-
/// >
2926
public string Query { get; set; }
3027

3128
/// <inheritdoc cref="ISqlRequest.TimeZone" />
32-
/// >
3329
public string TimeZone { get; set; }
30+
31+
/// <inheritdoc cref="ISqlRequest.RuntimeFields" />
32+
public IRuntimeFields RuntimeFields { get; set; }
3433
}
3534

3635
public partial class TranslateSqlDescriptor
@@ -42,6 +41,7 @@ protected sealed override void RequestDefaults(TranslateSqlRequestParameters par
4241
QueryContainer ISqlRequest.Filter { get; set; }
4342
string ISqlRequest.Query { get; set; }
4443
string ISqlRequest.TimeZone { get; set; }
44+
IRuntimeFields ISqlRequest.RuntimeFields { get; set; }
4545

4646
/// <inheritdoc cref="ISqlRequest.Query" />
4747
/// >
@@ -59,5 +59,9 @@ protected sealed override void RequestDefaults(TranslateSqlRequestParameters par
5959
/// >
6060
public TranslateSqlDescriptor Filter<T>(Func<QueryContainerDescriptor<T>, QueryContainer> querySelector)
6161
where T : class => Assign(querySelector, (a, v) => a.Filter = v?.Invoke(new QueryContainerDescriptor<T>()));
62+
63+
/// <inheritdoc cref="ISqlRequest.RuntimeFields" />
64+
public TranslateSqlDescriptor RuntimeFields<TSource>(Func<RuntimeFieldsDescriptor<TSource>, IPromise<IRuntimeFields>> runtimeFieldsSelector) where TSource : class =>
65+
Assign(runtimeFieldsSelector, (a, v) => a.RuntimeFields = v?.Invoke(new RuntimeFieldsDescriptor<TSource>())?.Value);
6266
}
6367
}

tests/Tests/XPack/Sql/QuerySql/QuerySqlApiTests.cs

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information
44

55
using System;
6+
using System.Linq;
67
using Elastic.Elasticsearch.Xunit.XunitPlumbing;
78
using Elasticsearch.Net;
89
using FluentAssertions;
@@ -15,9 +16,7 @@
1516

1617
namespace Tests.XPack.Sql.QuerySql
1718
{
18-
//[SkipVersion("<6.4.0", "")]
19-
// TODO: unskip when https://github.com/elastic/elasticsearch/issues/44320 is fixed
20-
[SkipVersion(">1.0.0", "open issue https://github.com/elastic/elasticsearch/issues/44320")]
19+
[SkipVersion("<6.4.0", "Added in 6.4.0")]
2120
public class QuerySqlApiTests : ApiIntegrationTestBase<XPackCluster, QuerySqlResponse, IQuerySqlRequest, QuerySqlDescriptor, QuerySqlRequest>
2221
{
2322
private static readonly string SqlQuery =
@@ -79,4 +78,86 @@ protected override void ExpectResponse(QuerySqlResponse response)
7978
}
8079
}
8180
}
81+
82+
[SkipVersion("<7.13.0", "Runtime mappings added in 7.13.0")]
83+
public class QuerySqlApiRuntimeMappingsTests : ApiIntegrationTestBase<XPackCluster, QuerySqlResponse, IQuerySqlRequest, QuerySqlDescriptor, QuerySqlRequest>
84+
{
85+
private static readonly string SqlQuery =
86+
$@"SELECT numberOfCommits, doublesCommits FROM {TestValueHelper.ProjectsIndex}";
87+
88+
public QuerySqlApiRuntimeMappingsTests(XPackCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
89+
90+
protected override bool ExpectIsValid => true;
91+
92+
protected override object ExpectJson { get; } = new
93+
{
94+
query = SqlQuery,
95+
fetch_size = 5,
96+
runtime_mappings = new
97+
{
98+
doublesCommits = new
99+
{
100+
script = new
101+
{
102+
source = "emit((long)(doc['numberOfCommits'].value * 2))"
103+
},
104+
type = "long"
105+
}
106+
}
107+
};
108+
109+
protected override int ExpectStatusCode => 200;
110+
111+
protected override Func<QuerySqlDescriptor, IQuerySqlRequest> Fluent => d => d
112+
.Query(SqlQuery)
113+
.FetchSize(5)
114+
.RuntimeFields(rtf => rtf.RuntimeField("doublesCommits", FieldType.Long, s => s.Script("emit((long)(doc['numberOfCommits'].value * 2))")));
115+
116+
protected override HttpMethod HttpMethod => HttpMethod.POST;
117+
118+
protected override QuerySqlRequest Initializer => new()
119+
{
120+
Query = SqlQuery,
121+
FetchSize = 5,
122+
RuntimeFields = new RuntimeFields
123+
{
124+
{ "doublesCommits", new RuntimeField
125+
{
126+
Type = FieldType.Long,
127+
Script = new InlineScript("emit((long)(doc['numberOfCommits'].value * 2))")
128+
}
129+
}
130+
}
131+
};
132+
133+
protected override string UrlPath => "/_sql";
134+
135+
protected override LazyResponses ClientUsage() => Calls(
136+
(client, f) => client.Sql.Query(f),
137+
(client, f) => client.Sql.QueryAsync(f),
138+
(client, r) => client.Sql.Query(r),
139+
(client, r) => client.Sql.QueryAsync(r)
140+
);
141+
142+
protected override void ExpectResponse(QuerySqlResponse response)
143+
{
144+
response.Cursor.Should().NotBeNullOrWhiteSpace("response cursor");
145+
response.Rows.Should().NotBeNullOrEmpty();
146+
147+
response.Columns.Single(x => x.Name == "numberOfCommits").Type.Should().Be("integer");
148+
response.Columns.Single(x => x.Name == "doublesCommits").Type.Should().Be("long");
149+
150+
foreach (var r in response.Rows)
151+
{
152+
r.Should().NotBeNull().And.HaveCount(2);
153+
var numberOfCommits = r[0].As<int?>();
154+
var doublesCommits = r[1].As<long?>();
155+
156+
if (!numberOfCommits.HasValue) continue;
157+
158+
doublesCommits.HasValue.Should().BeTrue();
159+
doublesCommits!.Value.Should().Be(numberOfCommits.Value * 2);
160+
}
161+
}
162+
}
82163
}

0 commit comments

Comments
 (0)