Skip to content

Commit fb937f4

Browse files
committed
Clean up and simplify FieldSelection, re-implement it on GetResponse
1 parent 184eb11 commit fb937f4

File tree

8 files changed

+87
-75
lines changed

8 files changed

+87
-75
lines changed

src/Nest/CommonAbstractions/Fields/FieldSelection.cs

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,69 +10,63 @@ namespace Nest
1010
{
1111
public interface IFieldSelection<out T>
1212
{
13-
/// <summary>
14-
/// As of elasticsearch fields are always returned as an array. except for internal metadata values such as routing.
15-
/// </summary>
16-
/// <typeparam name="K">The type to return the value as, remember that if your field is a string K should be string[]</typeparam>
17-
K FieldValues<K>(string path);
18-
13+
K[] FieldValues<K>(string path);
14+
15+
K FieldValue<K>(string path);
16+
1917
K[] FieldValues<TBindTo, K>(Expression<Func<TBindTo, object>> objectPath)
2018
where TBindTo : class;
2119

20+
K FieldValue<TBindTo, K>(Expression<Func<TBindTo, object>> objectPath)
21+
where TBindTo : class;
22+
2223
IDictionary<string, object> FieldValuesDictionary { get; set; }
2324
}
2425

2526
public class FieldSelection<T> : IFieldSelection<T>
2627
{
28+
private IFieldSelection<T> Self => this;
29+
2730
private ElasticInferrer Infer { get; set; }
28-
public FieldSelection(IConnectionSettingsValues settings, IDictionary<string, object> valuesDictionary = null)
29-
{
30-
this.Infer = settings.Inferrer;
31-
((IFieldSelection<T>)this).FieldValuesDictionary = valuesDictionary;
32-
}
3331

34-
[JsonConverter(typeof(VerbatimDictionaryKeysJsonConverter))]
3532
IDictionary<string, object> IFieldSelection<T>.FieldValuesDictionary { get; set; }
3633

37-
/// <summary>
38-
/// As of elasticsearch fields are always returned as an array. except for internal metadata values such as routing.
39-
/// </summary>
40-
/// <typeparam name="K">The type to return the value as, remember that if your field is a string K should be string[]</typeparam>
41-
public K FieldValues<K>(string path)
34+
public FieldSelection(ElasticInferrer inferrer, IDictionary<string, object> valuesDictionary = null)
4235
{
43-
return this.FieldArray<K>(path);
36+
this.Infer = inferrer;
37+
this.Self.FieldValuesDictionary = valuesDictionary;
4438
}
4539

46-
/// <summary>
47-
/// As of elasticsearch fields are always returned as an array.
48-
/// except for internal metadata values such as routing.
49-
/// </summary>
40+
public K[] FieldValues<K>(string path)
41+
{
42+
return this.FieldArray<K[]>(path);
43+
}
44+
45+
public K FieldValue<K>(string path) => FieldValues<K>(path).FirstOrDefault();
46+
47+
5048
public K[] FieldValues<TBindTo, K>(Expression<Func<TBindTo, object>> objectPath)
5149
where TBindTo : class
5250
{
5351
var path = this.Infer.Field(objectPath);
5452
return this.FieldArray<K[]>(path);
5553
}
5654

57-
/// <summary>
58-
/// As of elasticsearch fields are always returned as an array.
59-
/// except for internal metadata values such as routing.
60-
/// </summary>
55+
public K FieldValue<TBindTo, K>(Expression<Func<TBindTo, object>> objectPath)
56+
where TBindTo : class => FieldValues<TBindTo, K>(objectPath).FirstOrDefault();
57+
6158
public K[] FieldValues<K>(Expression<Func<T, K>> objectPath)
6259
{
6360
var path = this.Infer.Field(objectPath);
6461
return this.FieldArray<K[]>(path);
6562
}
6663

67-
/// <summary>
68-
/// As of elasticsearch fields are always returned as an array. except for internal metadata values such as routing.
69-
/// </summary>
70-
/// <typeparam name="K">The type to return the value as, remember that if your field is a string K should be string[]</typeparam>
64+
public K FieldValue<K>(Expression<Func<T, K>> objectPath) => FieldValues<K>(objectPath).FirstOrDefault();
65+
7166
private K FieldArray<K>(string path)
7267
{
73-
var fieldValues = ((IFieldSelection<T>)this).FieldValuesDictionary;
7468
object o;
75-
if (fieldValues != null && fieldValues.TryGetValue(path, out o))
69+
if (this.Self.FieldValuesDictionary != null && this.Self.FieldValuesDictionary.TryGetValue(path, out o))
7670
{
7771
var t = typeof(K);
7872
if (o is JArray && t.GetInterfaces().Contains(typeof(IEnumerable)))

src/Nest/CommonAbstractions/SerializationBehavior/StatefulDeserialization/ConcreteTypeConverter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
8080

8181
var instance = (Hit<T>)(typeof(Hit<T>).CreateInstance());
8282
serializer.Populate(reader, instance);
83-
instance.Fields = new FieldSelection<T>(serializer.GetConnectionSettings(), instance._fields);
83+
instance.Fields = new FieldSelection<T>(serializer.GetConnectionSettings().Inferrer, instance._fields);
8484
return instance;
8585
}
8686

@@ -160,7 +160,7 @@ internal static Type GetConcreteTypeUsingSelector<T>(
160160
dynamic d = jObject;
161161
var fields = jObject["fields"];
162162
var fieldSelectionData = fields?.ToObject<IDictionary<string, object>>();
163-
var sel = new FieldSelection<T>(settings, fieldSelectionData);
163+
var sel = new FieldSelection<T>(settings.Inferrer, fieldSelectionData);
164164
var hitDynamic = new Hit<dynamic>();
165165
//favor manual mapping over doing Populate twice.
166166
hitDynamic._fields = fieldSelectionData;

src/Nest/Document/Multiple/MultiGet/Response/MultiGetHit.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ public interface IMultiGetHit<out T> where T : class
2121
public class MultiGetHit<T> : IMultiGetHit<T>
2222
where T : class
2323
{
24-
//[JsonProperty(PropertyName = "fields")]
2524
public IFieldSelection<T> FieldSelection { get; internal set; }
2625

2726
[JsonProperty(PropertyName = "_source")]

src/Nest/Document/Multiple/MultiGet/Response/MultiGetHitJsonConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ private static void CreateMultiHit<T>(MultiHitTuple tuple, JsonSerializer serial
4141
serializer.Populate(reader, hit);
4242

4343
var settings = serializer.GetConnectionSettings();
44-
var f = new FieldSelection<T>(settings);
44+
var f = new FieldSelection<T>(settings.Inferrer);
4545
var source = tuple.Hit["fields"];
4646
if (source != null)
4747
{

src/Nest/Document/Single/Get/ElasticClient-Get.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Threading.Tasks;
33
using Elasticsearch.Net;
4+
using System.IO;
45

56
namespace Nest
67
{
@@ -33,9 +34,10 @@ public IGetResponse<T> Get<T>(DocumentPath<T> document, Func<GetDescriptor<T>, I
3334
this.Get<T>(selector.InvokeOrDefault(new GetDescriptor<T>(document)));
3435

3536
/// <inheritdoc/>
36-
public IGetResponse<T> Get<T>(IGetRequest request) where T : class =>
37+
public IGetResponse<T> Get<T>(IGetRequest request) where T : class =>
3738
this.Dispatcher.Dispatch<IGetRequest, GetRequestParameters, GetResponse<T>>(
3839
request,
40+
(r, s) => DeserializeGetResponse<T>(s),
3941
(p, d) => this.LowLevelDispatch.GetDispatch<GetResponse<T>>(p)
4042
);
4143

@@ -44,10 +46,19 @@ public Task<IGetResponse<T>> GetAsync<T>(DocumentPath<T> document, Func<GetDescr
4446
this.GetAsync<T>(selector.InvokeOrDefault(new GetDescriptor<T>(document)));
4547

4648
/// <inheritdoc/>
47-
public Task<IGetResponse<T>> GetAsync<T>(IGetRequest request) where T : class =>
49+
public Task<IGetResponse<T>> GetAsync<T>(IGetRequest request) where T : class =>
4850
this.Dispatcher.DispatchAsync<IGetRequest, GetRequestParameters, GetResponse<T>, IGetResponse<T>>(
4951
request,
52+
(r, s) => DeserializeGetResponse<T>(s),
5053
(p, d) => this.LowLevelDispatch.GetDispatchAsync<GetResponse<T>>(p)
5154
);
55+
56+
private GetResponse<T> DeserializeGetResponse<T>(Stream stream)
57+
where T : class
58+
{
59+
var response = Serializer.Deserialize<GetResponse<T>>(stream);
60+
response.Inferrer = this.ConnectionSettings.Inferrer;
61+
return response;
62+
}
5263
}
5364
}

src/Nest/Document/Single/Get/GetResponse.cs

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Linq.Expressions;
44
using Newtonsoft.Json;
5+
using System.Linq;
56

67
namespace Nest
78
{
@@ -19,7 +20,7 @@ public interface IGetResponse<T> : IResponse where T : class
1920
[JsonObject(MemberSerialization.OptIn)]
2021
public class GetResponse<T> : BaseResponse, IGetResponse<T> where T : class
2122
{
22-
private IDictionary<string, object> _fieldValues;
23+
internal ElasticInferrer Inferrer { get; set; }
2324

2425
[JsonProperty(PropertyName = "_index")]
2526
public string Index { get; private set; }
@@ -39,46 +40,20 @@ public class GetResponse<T> : BaseResponse, IGetResponse<T> where T : class
3940
[JsonProperty(PropertyName = "_source")]
4041
public T Source { get; private set; }
4142

42-
43-
[JsonConverter(typeof(VerbatimDictionaryKeysJsonConverter))]
4443
[JsonProperty(PropertyName = "fields")]
45-
internal IDictionary<string, object> FieldValues
46-
{
47-
get { return _fieldValues; }
48-
set { _fieldValues = value; }
49-
}
44+
private IDictionary<string, object> _rawFields;
5045

51-
//private FieldSelection<T> _fields = null;
46+
private FieldSelection<T> _fields = null;
5247
public FieldSelection<T> Fields
5348
{
5449
get
5550
{
56-
//TODO High Priority: Fix field selections
57-
throw new NotImplementedException("Fieldselections are broken in 2.0, responses no longer have settings");
58-
//if (_fields != null)
59-
// return _fields;
51+
if (_fields != null)
52+
return _fields;
6053

61-
//if (this.ApiCall == null)
62-
// return null;
63-
//var realSettings = this.ApiCall.Settings as IConnectionSettingsValues;
64-
65-
//_fields = new FieldSelection<T>(realSettings, FieldValues);
66-
//return _fields;
54+
_fields = new FieldSelection<T>(Inferrer, _rawFields);
55+
return _fields;
6756
}
6857
}
69-
70-
public K[] FieldValue<TBindTo, K>(Expression<Func<TBindTo, object>> objectPath)
71-
where TBindTo : class
72-
{
73-
if (this.Fields == null) return default(K[]);
74-
return this.Fields.FieldValues<TBindTo,K>(objectPath);
75-
}
76-
77-
public K FieldValue<K>(string path)
78-
{
79-
if (this.Fields == null) return default(K);
80-
return this.Fields.FieldValues<K>(path);
81-
}
82-
8358
}
8459
}

src/Nest/Document/Single/Index/ElasticClient-Index.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ public IIndexResponse Index<T>(T @object, Func<IndexDescriptor<T>, IIndexRequest
3838
/// <inheritdoc/>
3939
public IIndexResponse Index(IIndexRequest request) =>
4040
this.Dispatcher.Dispatch<IIndexRequest, IndexRequestParameters, IndexResponse>(
41-
request, this.LowLevelDispatch.IndexDispatch<IndexResponse>
41+
request,
42+
this.LowLevelDispatch.IndexDispatch<IndexResponse>
4243
);
4344

4445
/// <inheritdoc/>

src/Tests/Document/Single/Get/GetApiTests.cs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@
66
using Tests.Framework.Integration;
77
using Tests.Framework.MockData;
88
using Xunit;
9+
using FluentAssertions;
910

1011
namespace Tests.Document.Single.Get
1112
{
1213
[Collection(IntegrationContext.ReadOnly)]
1314
public class GetApiTests : ApiIntegrationTestBase<IGetResponse<Project>, IGetRequest, GetDescriptor<Project>, GetRequest<Project>>
1415
{
15-
private string ProjectId => Project.Projects.First().Name;
16+
protected string ProjectId => Project.Projects.First().Name;
1617

1718
public GetApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
1819
protected override LazyResponses ClientUsage() => Calls(
19-
fluent: (client, f) => client.Get<Project>(this.ProjectId),
20-
fluentAsync: (client, f) => client.GetAsync<Project>(this.ProjectId),
20+
fluent: (client, f) => client.Get<Project>(this.ProjectId, f),
21+
fluentAsync: (client, f) => client.GetAsync<Project>(this.ProjectId, f),
2122
request: (client, r) => client.Get<Project>(r),
2223
requestAsync: (client, r) => client.GetAsync<Project>(r)
2324
);
@@ -30,6 +31,37 @@ protected override LazyResponses ClientUsage() => Calls(
3031
protected override bool SupportsDeserialization => false;
3132

3233
protected override Func<GetDescriptor<Project>, IGetRequest> Fluent => null;
34+
3335
protected override GetRequest<Project> Initializer => new GetRequest<Project>(this.ProjectId);
36+
37+
protected override void ExpectResponse(IGetResponse<Project> response)
38+
{
39+
response.Source.Should().NotBeNull();
40+
response.Source.Name.Should().Be(ProjectId);
41+
}
42+
}
43+
44+
[Collection(IntegrationContext.ReadOnly)]
45+
public class GetApiFieldsTests : GetApiTests
46+
{
47+
public GetApiFieldsTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
48+
49+
protected override Func<GetDescriptor<Project>, IGetRequest> Fluent => g => g
50+
.Fields(
51+
p => p.Name,
52+
p => p.NumberOfCommits
53+
);
54+
55+
protected override GetRequest<Project> Initializer => new GetRequest<Project>(ProjectId)
56+
{
57+
Fields = Infer.Fields<Project>(p => p.Name, p => p.NumberOfCommits)
58+
};
59+
60+
protected override void ExpectResponse(IGetResponse<Project> response)
61+
{
62+
response.Fields.Should().NotBeNull();
63+
response.Fields.FieldValue(p => p.Name).Should().Be(ProjectId);
64+
response.Fields.FieldValue(p => p.NumberOfCommits).Should().BeGreaterThan(0);
65+
}
3466
}
3567
}

0 commit comments

Comments
 (0)