Skip to content

Commit dd94fc6

Browse files
committed
Merge pull request #1062 from elasticsearch/feature/property-name-mapping-in-code
Property names can now also be configured in code on ConnectionSettings
2 parents b778158 + 0218858 commit dd94fc6

File tree

5 files changed

+298
-28
lines changed

5 files changed

+298
-28
lines changed

src/Nest/Domain/Connection/ConnectionSettings.cs

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using System.Collections.ObjectModel;
33
using System.ComponentModel;
44
using System.Linq;
5+
using System.Linq.Expressions;
6+
using System.Reflection;
57
using Elasticsearch.Net.Connection;
68
using Elasticsearch.Net.ConnectionPool;
79
using Nest.Resolvers;
@@ -25,7 +27,7 @@ public class ConnectionSettings : ConnectionSettings<ConnectionSettings>
2527
/// <para>You can also specify specific default index/alias names for types using .SetDefaultTypeIndices(</para>
2628
/// <para>If you do not specify this, NEST might throw a runtime exception if an explicit indexname was not provided for a call</para>
2729
/// </param>
28-
public ConnectionSettings(Uri uri = null, string defaultIndex = null)
30+
public ConnectionSettings(Uri uri = null, string defaultIndex = null)
2931
: base(uri, defaultIndex)
3032
{
3133
}
@@ -39,18 +41,19 @@ public ConnectionSettings(Uri uri = null, string defaultIndex = null)
3941
/// <para>You can also specify specific default index/alias names for types using .SetDefaultTypeIndices(</para>
4042
/// <para>If you do not specify this, NEST might throw a runtime exception if an explicit indexname was not provided for a call</para>
4143
/// </param>
42-
public ConnectionSettings(IConnectionPool connectionPool, string defaultIndex = null) : base(connectionPool, defaultIndex)
44+
public ConnectionSettings(IConnectionPool connectionPool, string defaultIndex = null)
45+
: base(connectionPool, defaultIndex)
4346
{
44-
47+
4548
}
4649
}
4750
/// <summary>
4851
/// Control how NEST's behaviour.
4952
/// </summary>
5053
[Browsable(false)]
5154
[EditorBrowsable(EditorBrowsableState.Never)]
52-
public class ConnectionSettings<T> : ConnectionConfiguration<T> , IConnectionSettingsValues
53-
where T : ConnectionSettings<T>
55+
public class ConnectionSettings<T> : ConnectionConfiguration<T>, IConnectionSettingsValues
56+
where T : ConnectionSettings<T>
5457
{
5558
private string _defaultIndex;
5659
string IConnectionSettingsValues.DefaultIndex
@@ -87,24 +90,28 @@ string IConnectionSettingsValues.DefaultIndex
8790
private ReadOnlyCollection<Func<Type, JsonConverter>> _contractConverters;
8891
ReadOnlyCollection<Func<Type, JsonConverter>> IConnectionSettingsValues.ContractConverters { get { return _contractConverters; } }
8992

90-
public ConnectionSettings(IConnectionPool connectionPool, string defaultIndex) : base(connectionPool)
93+
private FluentDictionary<MemberInfo, string> _propertyNames = new FluentDictionary<MemberInfo, string>();
94+
FluentDictionary<MemberInfo, string> IConnectionSettingsValues.PropertyNames { get { return _propertyNames; } }
95+
96+
public ConnectionSettings(IConnectionPool connectionPool, string defaultIndex)
97+
: base(connectionPool)
9198
{
9299
if (!defaultIndex.IsNullOrEmpty())
93100
this.SetDefaultIndex(defaultIndex);
94-
95-
this._defaultTypeNameInferrer = (t => t.Name.ToLowerInvariant());
96-
this._defaultPropertyNameInferrer = (p => p.ToCamelCase());
101+
102+
this._defaultTypeNameInferrer = (t => t.Name.ToLowerInvariant());
103+
this._defaultPropertyNameInferrer = (p => p.ToCamelCase());
97104
this._defaultIndices = new FluentDictionary<Type, string>();
98105
this._defaultTypeNames = new FluentDictionary<Type, string>();
99106

100107
this._modifyJsonSerializerSettings = (j) => { };
101108
this._contractConverters = Enumerable.Empty<Func<Type, JsonConverter>>().ToList().AsReadOnly();
102109
this._inferrer = new ElasticInferrer(this);
103110
}
104-
public ConnectionSettings(Uri uri, string defaultIndex)
111+
public ConnectionSettings(Uri uri, string defaultIndex)
105112
: this(new SingleNodeConnectionPool(uri ?? new Uri("http://localhost:9200")), defaultIndex)
106113
{
107-
114+
108115
}
109116

110117
/// <summary>
@@ -197,5 +204,34 @@ public T MapDefaultTypeNames(Action<FluentDictionary<Type, string>> mappingSelec
197204
mappingSelector(this._defaultTypeNames);
198205
return (T)this;
199206
}
207+
208+
public T MapPropertyNamesFor<TDocument>(Action<FluentDictionary<Expression<Func<TDocument, object>>, string>> propertiesSelector)
209+
{
210+
propertiesSelector.ThrowIfNull("propertiesSelector");
211+
var properties = new FluentDictionary<Expression<Func<TDocument, object>>, string>();
212+
propertiesSelector(properties);
213+
foreach (var p in properties)
214+
{
215+
var e = p.Key;
216+
var memberInfoResolver = new MemberInfoResolver(this, e);
217+
if (memberInfoResolver.Members.Count > 1)
218+
throw new ArgumentException("MapPropertyNameFor can only map direct properties");
219+
220+
if (memberInfoResolver.Members.Count < 1)
221+
throw new ArgumentException("Expression {0} does contain any member access".F(e));
222+
223+
var memberInfo = memberInfoResolver.Members.Last();
224+
if (_propertyNames.ContainsKey(memberInfo))
225+
{
226+
var mappedAs = _propertyNames[memberInfo];
227+
var typeName = typeof (TDocument).Name;
228+
throw new ArgumentException("Property mapping '{0}' on type {3} can not be mapped to '{1}' already mapped as '{2}'"
229+
.F(e, p.Value, mappedAs, typeName));
230+
}
231+
_propertyNames.Add(memberInfo, p.Value);
232+
233+
}
234+
return (T) this;
235+
}
200236
}
201237
}

src/Nest/Domain/Connection/IConnectionSettingsValues.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.ObjectModel;
3+
using System.Reflection;
34
using Elasticsearch.Net.Connection;
45
using Newtonsoft.Json;
56

@@ -10,6 +11,7 @@ public interface IConnectionSettingsValues : IConnectionConfigurationValues
1011
ElasticInferrer Inferrer { get; }
1112
FluentDictionary<Type, string> DefaultIndices { get; }
1213
FluentDictionary<Type, string> DefaultTypeNames { get; }
14+
FluentDictionary<MemberInfo, string> PropertyNames { get; }
1315
string DefaultIndex { get; }
1416
Func<string, string> DefaultPropertyNameInferrer { get; }
1517
Func<Type, string> DefaultTypeNameInferrer { get; }

src/Nest/Resolvers/PropertyNameResolver.cs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,17 @@ public string Resolve(MemberInfo info)
5959
{
6060
if (info == null)
6161
return null;
62-
62+
6363
var name = info.Name;
64-
var resolvedName = _settings.DefaultPropertyNameInferrer(name);
64+
string resolvedName = null;
65+
if (_settings.PropertyNames.TryGetValue(info, out resolvedName))
66+
return resolvedName;
67+
6568
var att = ElasticAttributes.Property(info);
6669
if (att != null && !att.Name.IsNullOrEmpty())
67-
resolvedName = att.Name;
70+
return att.Name;
6871

69-
return resolvedName;
72+
return _settings.DefaultPropertyNameInferrer(name);
7073
}
7174

7275
public string ResolveToLastToken(MemberInfo info)
@@ -110,19 +113,7 @@ protected override Expression VisitMemberAccess(MemberExpression expression, Sta
110113
{
111114
if (stack != null)
112115
{
113-
var name = expression.Member.Name;
114-
var resolvedName = this._settings.DefaultPropertyNameInferrer(name);
115-
116-
var att = ElasticAttributes.Property(expression.Member);
117-
if (att != null)
118-
{
119-
properties.Push(att);
120-
}
121-
if (att != null && !att.Name.IsNullOrEmpty())
122-
{
123-
124-
resolvedName = att.Name;
125-
}
116+
var resolvedName = this.Resolve(expression.Member);
126117
stack.Push(resolvedName);
127118
}
128119
return base.VisitMemberAccess(expression, stack, properties);
@@ -162,11 +153,35 @@ protected override Expression VisitMethodCall(MethodCallExpression m, Stack<stri
162153
}
163154
return base.VisitMethodCall(m, stack, properties);
164155
}
156+
165157
private static bool IsLinqOperator(MethodInfo method)
166158
{
167159
if (method.DeclaringType != typeof(Queryable) && method.DeclaringType != typeof(Enumerable))
168160
return false;
169161
return Attribute.GetCustomAttribute(method, typeof(ExtensionAttribute)) != null;
170162
}
171163
}
164+
165+
166+
/// <summary>
167+
/// Resolves member infos in an expression, instance may NOT be shared.
168+
/// </summary>
169+
public class MemberInfoResolver : PropertyNameResolver
170+
{
171+
private readonly IList<MemberInfo> _members = new List<MemberInfo>();
172+
public IList<MemberInfo> Members { get { return _members; } }
173+
174+
public MemberInfoResolver(IConnectionSettingsValues settings, Expression expression) : base(settings)
175+
{
176+
var stack = new Stack<string>();
177+
var properties = new Stack<IElasticPropertyAttribute>();
178+
base.Visit(expression, stack, properties);
179+
}
180+
181+
protected override Expression VisitMemberAccess(MemberExpression expression, Stack<string> stack, Stack<IElasticPropertyAttribute> properties)
182+
{
183+
this._members.Add(expression.Member);
184+
return base.VisitMemberAccess(expression, stack, properties);
185+
}
186+
}
172187
}

0 commit comments

Comments
 (0)