Skip to content

Commit 7890fbe

Browse files
authored
Add SQL APIs (#6165)
* Add SQL APIs Does not include SQL Translate API at this stage.
1 parent f8f94f0 commit 7890fbe

26 files changed

+1891
-62
lines changed

build/scripts/Targets.fs

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ module Main =
4949
Tests.SetTestEnvironmentVariables parsed
5050

5151
let testChain = ["clean"; "version"; "restore"; "full-build"; ]
52-
let buildChain = ["test"; "inherit-doc"; "documentation"; ]
52+
let buildChain = ["test"; "inherit-doc"; ]
5353
let releaseChain =
5454
[
5555
"build";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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.Collections.Generic;
6+
using System.Text.Json.Serialization;
7+
8+
namespace Elastic.Clients.Elasticsearch.Sql;
9+
10+
public partial class SqlGetAsyncResponse
11+
{
12+
[JsonInclude]
13+
[JsonPropertyName("rows")]
14+
public IReadOnlyCollection<SqlRow> Rows { get; init; }
15+
}

src/Elastic.Clients.Elasticsearch/Common/LazyDocument.cs

+12-56
Original file line numberDiff line numberDiff line change
@@ -5,81 +5,37 @@
55
using System;
66
using System.Text.Json;
77
using System.Text.Json.Serialization;
8-
using System.Threading;
9-
using System.Threading.Tasks;
10-
using Elastic.Transport;
118

129
namespace Elastic.Clients.Elasticsearch
1310
{
1411
/// <summary>
15-
/// A lazily deserialized document.
12+
/// <para>A lazily deserialized document.</para>
13+
/// <para>Holds raw JSON bytes which can be lazily converted to a specific <see cref="Type"/> at a later time.</para>
1614
/// </summary>
1715
[JsonConverter(typeof(LazyDocumentConverter))]
18-
public class LazyDocument
16+
public readonly struct LazyDocument
1917
{
20-
private readonly Serializer _sourceSerializer;
21-
private readonly Serializer _requestResponseSerializer;
22-
private readonly IMemoryStreamFactory _memoryStreamFactory;
23-
2418
internal LazyDocument(byte[] bytes, IElasticsearchClientSettings settings)
2519
{
2620
Bytes = bytes;
27-
28-
_sourceSerializer = settings.SourceSerializer;
29-
_requestResponseSerializer = settings.RequestResponseSerializer;
30-
_memoryStreamFactory = settings.MemoryStreamFactory;
21+
Settings = settings;
3122
}
3223

33-
internal byte[] Bytes { get; }
34-
35-
internal T AsUsingRequestResponseSerializer<T>()
36-
{
37-
using var ms = _memoryStreamFactory.Create(Bytes);
38-
return _requestResponseSerializer.Deserialize<T>(ms);
39-
}
24+
internal byte[]? Bytes { get; }
25+
internal IElasticsearchClientSettings? Settings { get; }
4026

4127
/// <summary>
4228
/// Creates an instance of <typeparamref name="T" /> from this
4329
/// <see cref="LazyDocument" /> instance.
4430
/// </summary>
4531
/// <typeparam name="T">The type</typeparam>
46-
public T As<T>()
47-
{
48-
using var ms = _memoryStreamFactory.Create(Bytes);
49-
return _sourceSerializer.Deserialize<T>(ms);
50-
}
51-
52-
/// <summary>
53-
/// Creates an instance of <paramref name="objectType" /> from this
54-
/// <see cref="LazyDocument" /> instance.
55-
/// </summary>
56-
/// <param name="objectType">The type</param>
57-
public object As(Type objectType)
32+
public T? As<T>()
5833
{
59-
using var ms = _memoryStreamFactory.Create(Bytes);
60-
return _sourceSerializer.Deserialize(objectType, ms);
61-
}
34+
if (Bytes is null || Settings is null || Bytes.Length == 0)
35+
return default;
6236

63-
/// <summary>
64-
/// Creates an instance of <typeparamref name="T" /> from this
65-
/// <see cref="LazyDocument" /> instance.
66-
/// </summary>
67-
/// <typeparam name="T">The type</typeparam>
68-
public ValueTask<T> AsAsync<T>(CancellationToken ct = default)
69-
{
70-
using var ms = _memoryStreamFactory.Create(Bytes);
71-
return _sourceSerializer.DeserializeAsync<T>(ms, ct);
72-
}
73-
74-
/// <summary>
75-
/// Creates an instance of <paramref name="objectType" /> from this
76-
/// <see cref="LazyDocument" /> instance.
77-
/// </summary>
78-
/// <param name="objectType">The type</param>
79-
public ValueTask<object> AsAsync(Type objectType, CancellationToken ct = default)
80-
{
81-
using var ms = _memoryStreamFactory.Create(Bytes);
82-
return _sourceSerializer.DeserializeAsync(objectType, ms, ct);
37+
using var ms = Settings.MemoryStreamFactory.Create(Bytes);
38+
return Settings.SourceSerializer.Deserialize<T>(ms);
8339
}
8440
}
8541

@@ -101,6 +57,6 @@ public override LazyDocument Read(ref Utf8JsonReader reader, Type typeToConvert,
10157
return new LazyDocument(stream.ToArray(), _settings);
10258
}
10359

104-
public override void Write(Utf8JsonWriter writer, LazyDocument value, JsonSerializerOptions options) => throw new NotImplementedException();
60+
public override void Write(Utf8JsonWriter writer, LazyDocument value, JsonSerializerOptions options) => throw new NotImplementedException("We only ever expect to deserialize a LazyDocument on responses.");
10561
}
10662
}

src/Elastic.Clients.Elasticsearch/FutureGenerated/FutureGenerated.cs

+96-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,84 @@
1717
using System.Linq.Expressions;
1818
using System.IO;
1919

20+
namespace Elastic.Clients.Elasticsearch.Sql
21+
{
22+
public partial class SqlTranslateResponse
23+
{
24+
[JsonInclude]
25+
[JsonPropertyName("query")]
26+
public QueryContainer Query { get; set; }
27+
}
28+
29+
public static class SqlTranslateResponseExtensions
30+
{
31+
public static SearchRequest AsSearchRequest(this SqlTranslateResponse response)
32+
=> new()
33+
{
34+
Query = response.Query,
35+
//Size = response.Size,
36+
//Fields = response.Fields
37+
};
38+
}
39+
}
40+
41+
namespace Elastic.Clients.Elasticsearch
42+
{
43+
[JsonConverter(typeof(SourceConfigConverter))]
44+
public partial class SourceConfig
45+
{
46+
public bool HasBoolValue => Item1.HasValue;
47+
48+
public bool HasSourceFilterValue => Item2 is not null;
49+
50+
public bool TryGetBool(out bool? value)
51+
{
52+
if (Item1.HasValue)
53+
{
54+
value = Item1.Value;
55+
return true;
56+
}
57+
58+
value = null;
59+
return false;
60+
}
61+
62+
public bool TryGetSourceFilter(out SourceFilter? value)
63+
{
64+
if (Item2 is not null)
65+
{
66+
value = Item2;
67+
return true;
68+
}
69+
70+
value = null;
71+
return false;
72+
}
73+
}
74+
75+
internal class SourceConfigConverter : JsonConverter<SourceConfig>
76+
{
77+
public override SourceConfig? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
78+
{
79+
switch (reader.TokenType)
80+
{
81+
case JsonTokenType.True:
82+
case JsonTokenType.False:
83+
var value = reader.GetBoolean();
84+
return new SourceConfig(value);
85+
86+
case JsonTokenType.StartObject:
87+
var sourceFilter = JsonSerializer.Deserialize<SourceFilter>(ref reader, options);
88+
return new SourceConfig(sourceFilter);
89+
}
90+
91+
return null;
92+
}
93+
94+
public override void Write(Utf8JsonWriter writer, SourceConfig value, JsonSerializerOptions options) => throw new NotImplementedException();
95+
}
96+
}
97+
2098
namespace Elastic.Clients.Elasticsearch.Aggregations
2199
{
22100
//public partial class TopMetricsValue
@@ -1129,7 +1207,8 @@ public void Deserialize(ref Utf8JsonReader reader, JsonSerializerOptions options
11291207
public enum FieldType
11301208
{
11311209
Date,
1132-
Text
1210+
Text,
1211+
Long
11331212
}
11341213

11351214
public partial class CountRequest<TDocument> : CountRequest
@@ -1472,6 +1551,10 @@ public override FieldType Read(ref Utf8JsonReader reader, Type typeToConvert, Js
14721551
{
14731552
case "date":
14741553
return FieldType.Date;
1554+
case "long":
1555+
return FieldType.Long;
1556+
case "text":
1557+
return FieldType.Text;
14751558
}
14761559

14771560
ThrowHelper.ThrowJsonException("Unexpected field type value.");
@@ -1485,6 +1568,12 @@ public override void Write(Utf8JsonWriter writer, FieldType value, JsonSerialize
14851568
case FieldType.Date:
14861569
writer.WriteStringValue("date");
14871570
return;
1571+
case FieldType.Long:
1572+
writer.WriteStringValue("long");
1573+
return;
1574+
case FieldType.Text:
1575+
writer.WriteStringValue("text");
1576+
return;
14881577
}
14891578

14901579
writer.WriteNullValue();
@@ -1965,6 +2054,12 @@ public partial class MatchAllQuery
19652054
public static implicit operator QueryContainer(MatchAllQuery matchAllQuery) => new(matchAllQuery);
19662055
}
19672056

2057+
public partial class QueryContainer
2058+
{
2059+
// TODO - Generate more of these!
2060+
public TermQuery Term => Variant as TermQuery;
2061+
}
2062+
19682063
//public sealed partial class BoolQueryDescriptor
19692064
//{
19702065
// internal BoolQuery ToQuery()

src/Elastic.Clients.Elasticsearch/Serialization/DictionaryConverter.cs

+4
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ public override Dictionary<TKey, TValue> Read(
8989
{
9090
key = IndexName.Parse(propertyName) as TKey;
9191
}
92+
else if (typeof(TKey) == typeof(Field))
93+
{
94+
key = new Field(propertyName) as TKey;
95+
}
9296
else
9397
{
9498
key = (TKey)Activator.CreateInstance(typeof(TKey),

src/Elastic.Clients.Elasticsearch/Serialization/UnionConverter.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@ namespace Elastic.Clients.Elasticsearch;
1313

1414
internal sealed class UnionConverter : JsonConverterFactory
1515
{
16-
public override bool CanConvert(Type typeToConvert) => typeToConvert.Name == typeof(Union<,>).Name || (typeToConvert.BaseType is not null && typeToConvert.BaseType.Name == typeof(Union<,>).Name);
16+
private static readonly HashSet<Type> TypesToSkip = new HashSet<Type>
17+
{
18+
typeof(SourceConfig)
19+
};
20+
21+
public override bool CanConvert(Type typeToConvert) => !TypesToSkip.Contains(typeToConvert) &&
22+
(typeToConvert.Name == typeof(Union<,>).Name || (typeToConvert.BaseType is not null && typeToConvert.BaseType.Name == typeof(Union<,>).Name));
1723

1824
public override JsonConverter CreateConverter(
1925
Type type,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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 System.Collections.ObjectModel;
8+
using System.Text.Json;
9+
using System.Text.Json.Serialization;
10+
11+
namespace Elastic.Clients.Elasticsearch.Sql;
12+
13+
[JsonConverter(typeof(SqlRowConverter))]
14+
public sealed class SqlRow : ReadOnlyCollection<SqlValue>
15+
{
16+
public SqlRow(IList<SqlValue> list) : base(list) { }
17+
}
18+
19+
internal sealed class SqlRowConverter : JsonConverter<SqlRow>
20+
{
21+
public override SqlRow? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
22+
{
23+
if (reader.TokenType == JsonTokenType.Null)
24+
{
25+
reader.Read();
26+
return null;
27+
}
28+
29+
if (reader.TokenType == JsonTokenType.StartArray)
30+
{
31+
var values = new List<SqlValue>();
32+
33+
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
34+
{
35+
var value = JsonSerializer.Deserialize<SqlValue>(ref reader, options);
36+
values.Add(value);
37+
}
38+
39+
return new SqlRow(values);
40+
}
41+
42+
throw new JsonException($"Unexpected JSON token when deserializing {nameof(SqlRow)}.");
43+
}
44+
45+
public override void Write(Utf8JsonWriter writer, SqlRow value, JsonSerializerOptions options) => throw new NotImplementedException();
46+
}
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.Text.Json;
7+
using System.Text.Json.Serialization;
8+
9+
namespace Elastic.Clients.Elasticsearch.Sql;
10+
11+
[JsonConverter(typeof(SqlValueConverter))]
12+
public readonly struct SqlValue
13+
{
14+
private readonly LazyDocument _lazyDocument;
15+
16+
internal SqlValue(LazyDocument lazyDocument) => _lazyDocument = lazyDocument;
17+
18+
public T? As<T>() => _lazyDocument.As<T>();
19+
}
20+
21+
internal sealed class SqlValueConverter : JsonConverter<SqlValue>
22+
{
23+
public override SqlValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
24+
{
25+
if (reader.TokenType == JsonTokenType.Null)
26+
{
27+
reader.Read();
28+
return default;
29+
}
30+
31+
var lazyDoc = JsonSerializer.Deserialize<LazyDocument>(ref reader, options);
32+
return new SqlValue(lazyDoc);
33+
}
34+
35+
public override void Write(Utf8JsonWriter writer, SqlValue value, JsonSerializerOptions options) => throw new NotImplementedException();
36+
}

src/Elastic.Clients.Elasticsearch/_Generated/Api/ApiUrlsLookup.g.cs

+5
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ internal static class ApiUrlsLookups
4646
internal static ApiUrls NoNamespacePing = new ApiUrls(new[] { "/" });
4747
internal static ApiUrls NoNamespaceSearch = new ApiUrls(new[] { "/_search", "/{index}/_search" });
4848
internal static ApiUrls NoNamespaceGetSource = new ApiUrls(new[] { "/{index}/_source/{id}" });
49+
internal static ApiUrls SqlClearCursor = new ApiUrls(new[] { "/_sql/close" });
50+
internal static ApiUrls SqlDeleteAsync = new ApiUrls(new[] { "/_sql/async/delete/{id}" });
51+
internal static ApiUrls SqlGetAsync = new ApiUrls(new[] { "/_sql/async/{id}" });
52+
internal static ApiUrls SqlGetAsyncStatus = new ApiUrls(new[] { "/_sql/async/status/{id}" });
53+
internal static ApiUrls SqlQuery = new ApiUrls(new[] { "/_sql" });
4954
internal static ApiUrls NoNamespaceUpdate = new ApiUrls(new[] { "/{index}/_update/{id}" });
5055
}
5156
}

src/Elastic.Clients.Elasticsearch/_Generated/Api/SourceRequest.g.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,4 @@ protected override void Serialize(Utf8JsonWriter writer, JsonSerializerOptions o
199199
{
200200
}
201201
}
202-
}
202+
}

0 commit comments

Comments
 (0)