Skip to content

Commit b9fded9

Browse files
committed
Merge branch 'feature/2.x-perf-improvements' into 2.x
2 parents fced23c + 383623e commit b9fded9

File tree

15 files changed

+162
-115
lines changed

15 files changed

+162
-115
lines changed

src/Nest/CommonAbstractions/Extensions/JsonExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public static IConnectionSettingsValues GetConnectionSettings(this JsonSerialize
1414
{
1515
var contract = serializer.ContractResolver as ElasticContractResolver;
1616
if (contract?.ConnectionSettings == null)
17-
throw new Exception("If you use a custom contract resolver be sure to subclass from ElasticResolver");
17+
throw new Exception("If you use a custom contract resolver be sure to subclass from " + nameof(ElasticContractResolver));
1818
return contract.ConnectionSettings;
1919
}
2020

src/Nest/CommonAbstractions/Extensions/TypeExtensions.cs

+22-21
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,21 @@ namespace Nest
1212
{
1313
internal static class TypeExtensions
1414
{
15-
private static MethodInfo GetActivatorMethodInfo = typeof(TypeExtensions).GetMethod("GetActivator", BindingFlags.Static | BindingFlags.NonPublic);
15+
private static readonly MethodInfo GetActivatorMethodInfo =
16+
typeof(TypeExtensions).GetMethod(nameof(GetActivator), BindingFlags.Static | BindingFlags.NonPublic);
1617

17-
private static ConcurrentDictionary<string, ObjectActivator<object>> _cachedActivators = new ConcurrentDictionary<string, ObjectActivator<object>>();
18-
private static ConcurrentDictionary<string, Type> _cachedGenericClosedTypes = new ConcurrentDictionary<string, Type>();
18+
private static readonly ConcurrentDictionary<string, ObjectActivator<object>> CachedActivators =
19+
new ConcurrentDictionary<string, ObjectActivator<object>>();
1920

21+
private static readonly ConcurrentDictionary<string, Type> CachedGenericClosedTypes =
22+
new ConcurrentDictionary<string, Type>();
2023

21-
private static ConcurrentDictionary<Type, IList<JsonProperty>> _cachedTypeProperties =
24+
private static readonly ConcurrentDictionary<Type, IList<JsonProperty>> CachedTypeProperties =
2225
new ConcurrentDictionary<Type, IList<JsonProperty>>();
2326

2427
//this contract is only used to resolve properties in class WE OWN.
2528
//these are not subject to change depending on what the user passes as connectionsettings
26-
private static ElasticContractResolver _jsonContract = new ElasticContractResolver(new ConnectionSettings(), null);
29+
private static readonly ElasticContractResolver JsonContract = new ElasticContractResolver(new ConnectionSettings(), null);
2730

2831
public delegate T ObjectActivator<out T>(params object[] args);
2932

@@ -37,10 +40,10 @@ internal static object CreateGenericInstance(this Type t, Type[] closeOver, para
3740
var argKey = closeOver.Aggregate(new StringBuilder(), (sb, gt) => sb.Append("--" + gt.FullName), sb => sb.ToString());
3841
var key = t.FullName + argKey;
3942
Type closedType;
40-
if (!_cachedGenericClosedTypes.TryGetValue(key, out closedType))
43+
if (!CachedGenericClosedTypes.TryGetValue(key, out closedType))
4144
{
4245
closedType = t.MakeGenericType(closeOver);
43-
_cachedGenericClosedTypes.TryAdd(key, closedType);
46+
CachedGenericClosedTypes.TryAdd(key, closedType);
4447
}
4548
return closedType.CreateInstance(args);
4649
}
@@ -50,25 +53,23 @@ internal static object CreateGenericInstance(this Type t, Type[] closeOver, para
5053
internal static object CreateInstance(this Type t, params object[] args)
5154
{
5255
ObjectActivator<object> activator;
53-
var argLength = args.Count();
54-
//var argKey = string.Join(",", args.Select(a => a.GetType().Name));
55-
var argKey = argLength;
56+
var argKey = args.Length;
5657
var key = argKey + "--" + t.FullName;
57-
if (_cachedActivators.TryGetValue(key, out activator))
58+
if (CachedActivators.TryGetValue(key, out activator))
5859
return activator(args);
59-
var generic = GetActivatorMethodInfo.MakeGenericMethod(t);
6060

61+
var generic = GetActivatorMethodInfo.MakeGenericMethod(t);
6162
var constructors = from c in t.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
6263
let p = c.GetParameters()
6364
let k = string.Join(",", p.Select(a => a.ParameterType.Name))
64-
where p.Count() == argLength //&& k == argKey
65+
where p.Length == args.Length
6566
select c;
67+
6668
var ctor = constructors.FirstOrDefault();
6769
if (ctor == null)
68-
throw new Exception("Cannot create an instance of " + t.FullName
69-
+ " because it has no constructor taking " + argLength + " arguments");
70+
throw new Exception($"Cannot create an instance of {t.FullName} because it has no constructor taking {args.Length} arguments");
7071
activator = (ObjectActivator<object>)generic.Invoke(null, new[] { ctor });
71-
_cachedActivators.TryAdd(key, activator);
72+
CachedActivators.TryAdd(key, activator);
7273
return activator(args);
7374
}
7475

@@ -85,7 +86,7 @@ private static ObjectActivator<T> GetActivator<T>(ConstructorInfo ctor)
8586
Expression[] argsExp =
8687
new Expression[paramsInfo.Length];
8788

88-
//pick each arg from the params array
89+
//pick each arg from the params array
8990
//and create a typed expression of them
9091
for (int i = 0; i < paramsInfo.Length; i++)
9192
{
@@ -118,10 +119,10 @@ private static ObjectActivator<T> GetActivator<T>(ConstructorInfo ctor)
118119
internal static IList<JsonProperty> GetCachedObjectProperties(this Type t, MemberSerialization memberSerialization = MemberSerialization.OptIn)
119120
{
120121
IList<JsonProperty> propertyDictionary;
121-
if (_cachedTypeProperties.TryGetValue(t, out propertyDictionary))
122+
if (CachedTypeProperties.TryGetValue(t, out propertyDictionary))
122123
return propertyDictionary;
123-
propertyDictionary = _jsonContract.PropertiesOfAll(t, memberSerialization);
124-
_cachedTypeProperties.TryAdd(t, propertyDictionary);
124+
propertyDictionary = JsonContract.PropertiesOfAll(t, memberSerialization);
125+
CachedTypeProperties.TryAdd(t, propertyDictionary);
125126
return propertyDictionary;
126127
}
127128

@@ -163,4 +164,4 @@ internal static IEnumerable<Type> GetInterfaces(this Type type)
163164
}
164165
#endif
165166
}
166-
}
167+
}

src/Nest/CommonAbstractions/Fields/FieldValues.cs

+6-6
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public K[] Values<T, K>(Expression<Func<T, K>> objectPath)
5151
}
5252

5353

54-
private static JsonSerializer ForceNoDateInferrence = new JsonSerializer
54+
private static readonly JsonSerializer ForceNoDateInferrence = new JsonSerializer
5555
{
5656
DateParseHandling = DateParseHandling.None
5757
};
@@ -62,21 +62,21 @@ private K FieldArray<K>(string field)
6262
if (this.BackingDictionary != null && this.BackingDictionary.TryGetValue(field, out o))
6363
{
6464
var t = typeof(K);
65-
if (o is JArray && t.GetInterfaces().Contains(typeof(IEnumerable)))
65+
var jArray = o as JArray;
66+
if (jArray != null && t.GetInterfaces().Contains(typeof(IEnumerable)))
6667
{
67-
var array = (JArray)o;
68-
if (typeof(K) == typeof(string[]) && array.Count > 0 && array.Any(p=>p.Type == JTokenType.Date))
68+
if (typeof(K) == typeof(string[]) && jArray.Any(p=>p.Type == JTokenType.Date))
6969
{
7070
// https://github.com/elastic/elasticsearch-net/issues/2155
7171
// o of type JArray has already decided the values are dates so there is no
7272
// way around this.
7373
// incredibly ugly and sad but the only way I found to cover this edgecase
74-
var s = array.Root.ToString();
74+
var s = jArray.Root.ToString();
7575
using (var sr = new StringReader(s))
7676
using (var jr = new JsonTextReader(sr) { DateParseHandling = DateParseHandling.None })
7777
return ForceNoDateInferrence.Deserialize<K>(jr);
7878
}
79-
return array.ToObject<K>();
79+
return jArray.ToObject<K>();
8080
}
8181
return (K)Convert.ChangeType(o, typeof(K));
8282
}

src/Nest/CommonAbstractions/Infer/Field/Field.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ string IUrlParameter.GetString(IConnectionConfigurationValues settings)
160160
var nestSettings = settings as IConnectionSettingsValues;
161161
if (nestSettings == null)
162162
throw new Exception("Tried to pass field name on querysting but it could not be resolved because no nest settings are available");
163-
var infer = new Inferrer(nestSettings);
164-
return infer.Field(this);
163+
164+
return nestSettings.Inferrer.Field(this);
165165
}
166166

167167
private void SetComparisonValue(object value)

src/Nest/CommonAbstractions/Infer/Id/IdJsonConverter.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
1717

1818
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
1919
{
20-
var id = value as Id;
21-
if (id == null)
20+
if (value == null)
2221
{
2322
writer.WriteNull();
2423
return;
2524
}
25+
26+
var id = (Id)value;
2627
if (id.Document != null)
2728
{
2829
var settings = serializer.GetConnectionSettings();

src/Nest/CommonAbstractions/Infer/IndexName/IndexName.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,14 @@ public bool EqualsMarker(IndexName other)
6969
return false;
7070
}
7171

72-
public string GetString(IConnectionConfigurationValues settings) => ((IUrlParameter)(Indices)(Indices.Index(this))).GetString(settings);
72+
public string GetString(IConnectionConfigurationValues settings)
73+
{
74+
var nestSettings = settings as IConnectionSettingsValues;
75+
if (nestSettings == null)
76+
throw new Exception("Tried to pass index name on querysting but it could not be resolved because no nest settings are available");
77+
78+
return nestSettings.Inferrer.IndexName(this);
79+
}
7380

7481
public static IndexName From<T>() => typeof(T);
7582

src/Nest/CommonAbstractions/Infer/IndexName/IndexNameResolver.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ public IndexNameResolver(IConnectionSettingsValues connectionSettings)
1616

1717
public string Resolve(IndexName i)
1818
{
19-
if (i == null || string.IsNullOrEmpty(i.Name))
20-
return this.Resolve(i.Type);
21-
return i.Name;
19+
return string.IsNullOrEmpty(i?.Name)
20+
? this.Resolve(i.Type)
21+
: i.Name;
2222
}
2323

2424
public string Resolve(Type type)

src/Nest/CommonAbstractions/Infer/Indices/Indices.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ string IUrlParameter.GetString(IConnectionConfigurationValues settings)
5656
{
5757
var nestSettings = settings as IConnectionSettingsValues;
5858
if (nestSettings == null)
59-
throw new Exception("Tried to pass field name on querysting but it could not be resolved because no nest settings are available");
59+
throw new Exception("Tried to pass index names on querysting but it could not be resolved because no nest settings are available");
6060

6161
var infer = nestSettings.Inferrer;
6262
var indices = many.Indices.Select(i => infer.IndexName(i)).Distinct();

src/Nest/CommonAbstractions/Infer/Inferrer.cs

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using System.Globalization;
45
using System.Linq;
6+
using Newtonsoft.Json;
7+
using Newtonsoft.Json.Serialization;
58

69
namespace Nest
710
{
@@ -12,13 +15,21 @@ public class Inferrer
1215
private TypeNameResolver TypeNameResolver { get; }
1316
private FieldResolver FieldResolver { get; }
1417

18+
// TODO: Find some better place for this
19+
internal ConcurrentDictionary<Type, JsonContract> Contracts { get; set; }
20+
internal ConcurrentDictionary<Type, Action<MultiGetHitJsonConverter.MultiHitTuple, JsonSerializer, ICollection<IMultiGetHit<object>>>> CreateMultiHitDelegates { get; set; }
21+
internal ConcurrentDictionary<Type, Action<MultiSearchResponseJsonConverter.SearchHitTuple, JsonSerializer, IDictionary<string, object>>> CreateSearchResponseDelegates { get; set; }
22+
1523
public Inferrer(IConnectionSettingsValues connectionSettings)
1624
{
1725
connectionSettings.ThrowIfNull(nameof(connectionSettings));
1826
this.IdResolver = new IdResolver(connectionSettings);
1927
this.IndexNameResolver = new IndexNameResolver(connectionSettings);
2028
this.TypeNameResolver = new TypeNameResolver(connectionSettings);
2129
this.FieldResolver = new FieldResolver(connectionSettings);
30+
this.Contracts = new ConcurrentDictionary<Type, JsonContract>();
31+
this.CreateMultiHitDelegates = new ConcurrentDictionary<Type, Action<MultiGetHitJsonConverter.MultiHitTuple, JsonSerializer, ICollection<IMultiGetHit<object>>>>();
32+
this.CreateSearchResponseDelegates = new ConcurrentDictionary<Type, Action<MultiSearchResponseJsonConverter.SearchHitTuple, JsonSerializer, IDictionary<string, object>>>();
2233
}
2334

2435
public string Field(Field field) => this.FieldResolver.Resolve(field);
@@ -28,7 +39,7 @@ public Inferrer(IConnectionSettingsValues connectionSettings)
2839
public string IndexName<T>() where T : class => this.IndexNameResolver.Resolve<T>();
2940

3041
public string IndexName(IndexName index) => this.IndexNameResolver.Resolve(index);
31-
42+
3243
public string Id<T>(T obj) where T : class => this.IdResolver.Resolve(obj);
3344

3445
public string Id(Type objType, object obj) => this.IdResolver.Resolve(objType, obj);

src/Nest/CommonAbstractions/Infer/TypeName/TypeName.cs

+9-2
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,18 @@ public bool EqualsString(string other)
7979
return !other.IsNullOrEmpty() && other == this.Name;
8080
}
8181

82-
string IUrlParameter.GetString(IConnectionConfigurationValues settings) => ((IUrlParameter)(Types)(Types.Type(this))).GetString(settings);
82+
string IUrlParameter.GetString(IConnectionConfigurationValues settings)
83+
{
84+
var nestSettings = settings as IConnectionSettingsValues;
85+
if (nestSettings == null)
86+
throw new Exception("Tried to pass type name on querystring but it could not be resolved because no nest settings are available");
87+
88+
return nestSettings.Inferrer.TypeName(this);
89+
}
8390

8491
public static TypeName From<T>() => typeof(T);
8592

8693
public Types And<T>() => new Types(new TypeName[] { this, typeof(T)});
8794
public Types And(TypeName type) => new Types(new TypeName[] { this, type });
8895
}
89-
}
96+
}

src/Nest/CommonAbstractions/Infer/Types/Types.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ string IUrlParameter.GetString(IConnectionConfigurationValues settings)
5757
var nestSettings = settings as IConnectionSettingsValues;
5858
if (nestSettings == null)
5959
throw new Exception("Tried to pass field name on querystring but it could not be resolved because no nest settings are available");
60-
var infer = new Inferrer(nestSettings);
61-
var types = this.Item2.Types.Select(t => infer.TypeName(t)).Distinct();
60+
61+
var types = this.Item2.Types.Select(t => nestSettings.Inferrer.TypeName(t)).Distinct();
6262
return string.Join(",", types);
6363
}
6464
);

src/Nest/CommonAbstractions/SerializationBehavior/ElasticContractResolver.cs

+37-35
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections;
3+
using System.Collections.Concurrent;
34
using System.Collections.Generic;
45
using System.Linq;
56
using System.Reflection;
@@ -15,7 +16,6 @@ public class ElasticContractResolver : DefaultContractResolver
1516
private readonly IList<Func<Type, JsonConverter>> _contractConverters;
1617
public static JsonSerializer Empty { get; } = new JsonSerializer();
1718

18-
1919
/// <summary>
2020
/// ConnectionSettings can be requested by JsonConverter's.
2121
/// </summary>
@@ -33,43 +33,45 @@ public ElasticContractResolver(IConnectionSettingsValues connectionSettings, ILi
3333
}
3434

3535
protected override JsonContract CreateContract(Type objectType)
36-
{
37-
JsonContract contract = base.CreateContract(objectType);
38-
39-
// this will only be called once and then cached
40-
41-
if (typeof(IDictionary).IsAssignableFrom(objectType) && !typeof(IIsADictionary).IsAssignableFrom(objectType))
42-
contract.Converter = new VerbatimDictionaryKeysJsonConverter();
43-
if (typeof (IEnumerable<QueryContainer>).IsAssignableFrom(objectType))
44-
contract.Converter = new QueryContainerCollectionJsonConverter();
45-
else if (objectType == typeof(ServerError))
46-
contract.Converter = new ServerErrorJsonConverter();
47-
else if (objectType == typeof(DateTime) ||
48-
objectType == typeof(DateTime?) ||
49-
objectType == typeof(DateTimeOffset) ||
50-
objectType == typeof(DateTimeOffset?))
51-
contract.Converter = new IsoDateTimeConverter();
52-
else if (objectType == typeof(TimeSpan) ||
53-
objectType == typeof(TimeSpan?))
54-
contract.Converter = new TimeSpanConverter();
55-
56-
if (this._contractConverters.HasAny())
57-
{
58-
foreach (var c in this._contractConverters)
36+
{
37+
// cache contracts per connection settings
38+
return this.ConnectionSettings.Inferrer.Contracts.GetOrAdd(objectType, o =>
39+
{
40+
var contract = base.CreateContract(o);
41+
42+
if (typeof(IDictionary).IsAssignableFrom(o) && !typeof(IIsADictionary).IsAssignableFrom(o))
43+
contract.Converter = new VerbatimDictionaryKeysJsonConverter();
44+
if (typeof(IEnumerable<QueryContainer>).IsAssignableFrom(o))
45+
contract.Converter = new QueryContainerCollectionJsonConverter();
46+
else if (o == typeof(ServerError))
47+
contract.Converter = new ServerErrorJsonConverter();
48+
else if (o == typeof(DateTime) ||
49+
o == typeof(DateTime?) ||
50+
o == typeof(DateTimeOffset) ||
51+
o == typeof(DateTimeOffset?))
52+
contract.Converter = new IsoDateTimeConverter();
53+
else if (o == typeof(TimeSpan) ||
54+
o == typeof(TimeSpan?))
55+
contract.Converter = new TimeSpanConverter();
56+
57+
if (this._contractConverters.HasAny())
5958
{
60-
var converter = c(objectType);
61-
if (converter == null)
62-
continue;
63-
contract.Converter = converter;
64-
break;
59+
foreach (var c in this._contractConverters)
60+
{
61+
var converter = c(o);
62+
if (converter == null)
63+
continue;
64+
contract.Converter = converter;
65+
break;
66+
}
6567
}
66-
}
67-
if (!objectType.FullName.StartsWith("Nest.", StringComparison.OrdinalIgnoreCase)) return contract;
68-
69-
else if (ApplyExactContractJsonAttribute(objectType, contract)) return contract;
70-
else if (ApplyContractJsonAttribute(objectType, contract)) return contract;
7168

72-
return contract;
69+
if (!o.FullName.StartsWith("Nest.", StringComparison.OrdinalIgnoreCase)) return contract;
70+
if (ApplyExactContractJsonAttribute(o, contract)) return contract;
71+
if (ApplyContractJsonAttribute(o, contract)) return contract;
72+
73+
return contract;
74+
});
7375
}
7476

7577
private bool ApplyExactContractJsonAttribute(Type objectType, JsonContract contract)

0 commit comments

Comments
 (0)