44
55using System ;
66using System . Diagnostics ;
7+ using System . Diagnostics . CodeAnalysis ;
78using System . Globalization ;
89using System . Linq . Expressions ;
910using System . Reflection ;
1011using System . Text . Json . Serialization ;
12+
1113using Elastic . Transport ;
1214
1315#if ELASTICSEARCH_SERVERLESS
1416namespace Elastic . Clients . Elasticsearch . Serverless ;
1517#else
18+
1619namespace Elastic . Clients . Elasticsearch ;
1720#endif
1821
1922[ JsonConverter ( typeof ( FieldConverter ) ) ]
20- [ DebuggerDisplay ( "{" + nameof ( DebugDisplay ) + ",nq}" ) ]
21- public sealed class Field : IEquatable < Field > , IUrlParameter
23+ [ DebuggerDisplay ( $ "{ nameof ( DebuggerDisplay ) } ,nq") ]
24+ public sealed class Field :
25+ IEquatable < Field > ,
26+ IUrlParameter
2227{
2328 private readonly object _comparisonValue ;
24- private readonly Type _type ;
29+ private readonly Type ? _type ;
2530
2631 // Pseudo and metadata fields
2732
@@ -30,143 +35,173 @@ public sealed class Field : IEquatable<Field>, IUrlParameter
3035 public static Field KeyField = new ( "_key" ) ;
3136 public static Field CountField = new ( "_count" ) ;
3237
33- public Field ( string name ) : this ( name , null , null ) { }
38+ /// <summary>
39+ /// The name of the field
40+ /// </summary>
41+ public string ? Name { get ; }
42+
43+ /// <summary>
44+ /// An expression from which the name of the field can be inferred
45+ /// </summary>
46+ public Expression ? Expression { get ; }
47+
48+ /// <summary>
49+ /// A property from which the name of the field can be inferred
50+ /// </summary>
51+ public PropertyInfo ? Property { get ; }
52+
53+ /// <summary>
54+ /// A boost to apply to the field
55+ /// </summary>
56+ public double ? Boost { get ; set ; }
57+
58+ /// <summary>
59+ /// A format to apply to the field.
60+ /// </summary>
61+ /// <remarks>
62+ /// Can be used only for Doc Value Fields Elasticsearch 6.4.0+
63+ /// </remarks>
64+ public string ? Format { get ; set ; }
65+
66+ internal bool CachableExpression { get ; }
67+
68+ #region Constructors
69+
70+ public Field ( string name ) : this ( name , null , null )
71+ {
72+ }
3473
35- public Field ( string name , double boost ) : this ( name , boost , null ) { }
74+ public Field ( string name , double boost ) : this ( name , boost , null )
75+ {
76+ }
3677
37- public Field ( string name , string format ) : this ( name , null , format ) { }
78+ public Field ( string name , string format ) : this ( name , null , format )
79+ {
80+ }
3881
3982 public Field ( string name , double ? boost , string ? format )
4083 {
41- name . ThrowIfNullOrEmpty ( nameof ( name ) ) ;
84+ if ( string . IsNullOrEmpty ( name ) )
85+ throw new ArgumentException ( $ "{ name } can not be null or empty.", nameof ( name ) ) ;
86+
4287 Name = ParseFieldName ( name , out var b ) ;
4388 Boost = b ?? boost ;
4489 Format = format ;
90+
4591 _comparisonValue = Name ;
4692 }
4793
48- public Field ( Expression expression , double ? boost = null , string format = null )
94+ public Field ( Expression expression , double ? boost = null , string ? format = null )
4995 {
5096 Expression = expression ?? throw new ArgumentNullException ( nameof ( expression ) ) ;
5197 Boost = boost ;
5298 Format = format ;
99+
53100 _comparisonValue = expression . ComparisonValueFromExpression ( out var type , out var cachable ) ;
54101 _type = type ;
102+
55103 CachableExpression = cachable ;
56104 }
57105
58- public Field ( PropertyInfo property , double ? boost = null , string format = null )
106+ public Field ( PropertyInfo property , double ? boost = null , string ? format = null )
59107 {
60108 Property = property ?? throw new ArgumentNullException ( nameof ( property ) ) ;
61109 Boost = boost ;
62110 Format = format ;
111+
63112 _comparisonValue = property ;
64113 _type = property . DeclaringType ;
65114 }
66115
67- /// <summary>
68- /// A boost to apply to the field
69- /// </summary>
70- public double ? Boost { get ; set ; }
116+ #endregion Constructors
71117
72- /// <summary>
73- /// A format to apply to the field.
74- /// </summary>
75- /// <remarks>
76- /// Can be used only for Doc Value Fields Elasticsearch 6.4.0+
77- /// </remarks>
78- public string ? Format { get ; set ; }
118+ #region Factory Methods
79119
80- internal bool CachableExpression { get ; }
120+ public static Field ? FromString ( string ? name ) => string . IsNullOrEmpty ( name ) ? null : new Field ( name ) ;
81121
82- /// <summary>
83- /// An expression from which the name of the field can be inferred
84- /// </summary>
85- public Expression Expression { get ; }
122+ public static Field ? FromExpression ( Expression ? expression ) => expression is null ? null : new Field ( expression ) ;
86123
87- /// <summary>
88- /// The name of the field
89- /// </summary>
90- public string Name { get ; }
124+ public static Field ? FromProperty ( PropertyInfo ? property ) => property is null ? null : new Field ( property ) ;
91125
92- /// <summary>
93- /// A property from which the name of the field can be inferred
94- /// </summary>
95- public PropertyInfo Property { get ; }
126+ #endregion Factory Methods
96127
97- internal string DebugDisplay =>
98- $ "{ Expression ? . ToString ( ) ?? PropertyDebug ?? Name } { ( Boost . HasValue ? "^" + Boost . Value : string . Empty ) } "
99- + $ "{ ( ! string . IsNullOrEmpty ( Format ) ? " format: " + Format : string . Empty ) } "
100- + $ "{ ( _type == null ? string . Empty : " typeof: " + _type . Name ) } ";
128+ #region Conversion Operators
101129
102- private string PropertyDebug => Property == null ? null : $ "PropertyInfo: { Property . Name } " ;
130+ public static implicit operator Field ? ( string ? name ) => FromString ( name ) ;
103131
104- public bool Equals ( Field other ) => _type != null
105- ? other != null && _type == other . _type && _comparisonValue . Equals ( other . _comparisonValue )
106- : other != null && _comparisonValue . Equals ( other . _comparisonValue ) ;
132+ public static implicit operator Field ? ( Expression ? expression ) => FromExpression ( expression ) ;
107133
108- string IUrlParameter . GetString ( ITransportConfiguration settings )
134+ public static implicit operator Field ? ( PropertyInfo ? property ) => FromProperty ( property ) ;
135+
136+ #endregion Conversion Operators
137+
138+ #region Combinator Methods
139+
140+ public Fields And ( Field field )
109141 {
110- if ( settings is not IElasticsearchClientSettings elasticsearchSettings )
111- {
112- throw new ArgumentNullException ( nameof ( settings ) ,
113- $ "Can not resolve { nameof ( Field ) } if no { nameof ( IElasticsearchClientSettings ) } is provided") ;
114- }
142+ if ( field is null )
143+ throw new ArgumentNullException ( nameof ( field ) ) ;
115144
116- return GetStringCore ( elasticsearchSettings ) ;
145+ return new ( [ this , field ] ) ;
117146 }
118147
119- private string GetStringCore ( IElasticsearchClientSettings settings )
148+ public Fields And < T , TValue > ( Expression < Func < T , TValue > > expression , double ? boost = null , string ? format = null )
120149 {
121- if ( settings is null )
122- {
123- throw new ArgumentNullException ( nameof ( settings ) ,
124- $ "Can not resolve { nameof ( Field ) } if no { nameof ( IElasticsearchClientSettings ) } is provided") ;
125- }
150+ if ( expression is null )
151+ throw new ArgumentNullException ( nameof ( expression ) ) ;
126152
127- return settings . Inferrer . Field ( this ) ;
153+ return new ( [ this , new Field ( expression , boost , format ) ] ) ;
128154 }
129155
130- public override string ToString ( ) => DebugDisplay ;
156+ public Fields And < T > ( Expression < Func < T , object > > expression , double ? boost = null , string ? format = null )
157+ {
158+ if ( expression is null )
159+ throw new ArgumentNullException ( nameof ( expression ) ) ;
131160
132- public Fields And ( Field field ) => new ( new [ ] { this , field } ) ;
161+ return new ( [ this , new Field ( expression , boost , format ) ] ) ;
162+ }
133163
134- public Fields And < T , TValue > ( Expression < Func < T , TValue > > field , double ? boost = null , string format = null )
135- where T : class =>
136- new ( new [ ] { this , new Field ( field , boost , format ) } ) ;
164+ public Fields And ( string field , double ? boost = null , string ? format = null )
165+ {
166+ if ( field is null )
167+ throw new ArgumentNullException ( nameof ( field ) ) ;
137168
138- public Fields And < T > ( Expression < Func < T , object > > field , double ? boost = null , string format = null )
139- where T : class =>
140- new ( new [ ] { this , new Field ( field , boost , format ) } ) ;
169+ return new ( [ this , new Field ( field , boost , format ) ] ) ;
170+ }
141171
142- public Fields And ( string field , double ? boost = null , string format = null ) =>
143- new ( new [ ] { this , new Field ( field , boost , format ) } ) ;
172+ public Fields And ( PropertyInfo property , double ? boost = null , string ? format = null )
173+ {
174+ if ( property is null )
175+ throw new ArgumentNullException ( nameof ( property ) ) ;
144176
145- public Fields And ( PropertyInfo property , double ? boost = null , string format = null ) =>
146- new ( new [ ] { this , new Field ( property , boost , format ) } ) ;
177+ return new ( [ this , new Field ( property , boost , format ) ] ) ;
178+ }
147179
148- private static string ParseFieldName ( string name , out double ? boost )
149- {
150- boost = null ;
151- if ( name == null )
152- return null ;
180+ #endregion Combinator Methods
153181
154- var caretIndex = name . IndexOf ( '^' ) ;
155- if ( caretIndex == - 1 )
156- return name ;
182+ #region Equality
157183
158- var parts = name . Split ( new [ ] { '^' } , 2 , StringSplitOptions . RemoveEmptyEntries ) ;
159- name = parts [ 0 ] ;
160- boost = double . Parse ( parts [ 1 ] , CultureInfo . InvariantCulture ) ;
161- return name ;
162- }
184+ public static bool operator == ( Field ? a , Field ? b ) => Equals ( a , b ) ;
163185
164- public static implicit operator Field ( string name ) => name . IsNullOrEmpty ( ) ? null : new Field ( name ) ;
186+ public static bool operator != ( Field ? a , Field ? b ) => ! Equals ( a , b ) ;
165187
166- public static implicit operator Field ( Expression expression ) =>
167- expression == null ? null : new Field ( expression ) ;
188+ public bool Equals ( Field ? other ) =>
189+ other switch
190+ {
191+ not null when _type is not null => ( _type == other . _type ) && _comparisonValue . Equals ( other . _comparisonValue ) ,
192+ not null when _type is null => _comparisonValue . Equals ( other . _comparisonValue ) ,
193+ _ => false
194+ } ;
168195
169- public static implicit operator Field ( PropertyInfo property ) => property == null ? null : new Field ( property ) ;
196+ public override bool Equals ( object ? obj ) =>
197+ obj switch
198+ {
199+ Field f => Equals ( f ) ,
200+ string s => Equals ( s ) ,
201+ Expression e => Equals ( e ) ,
202+ PropertyInfo p => Equals ( p ) ,
203+ _ => false
204+ } ;
170205
171206 public override int GetHashCode ( )
172207 {
@@ -178,22 +213,50 @@ public override int GetHashCode()
178213 }
179214 }
180215
181- public override bool Equals ( object obj )
216+ #endregion Equality
217+
218+ #region IUrlParameter
219+
220+ string IUrlParameter . GetString ( ITransportConfiguration settings )
182221 {
183- switch ( obj )
222+ if ( settings is not IElasticsearchClientSettings elasticsearchSettings )
184223 {
185- case string s :
186- return Equals ( s ) ;
187- case PropertyInfo p :
188- return Equals ( p ) ;
189- case Field f :
190- return Equals ( f ) ;
191- default :
192- return false ;
224+ throw new ArgumentNullException ( nameof ( settings ) ,
225+ $ "Can not resolve { nameof ( Field ) } if no { nameof ( IElasticsearchClientSettings ) } is provided") ;
193226 }
227+
228+ return elasticsearchSettings . Inferrer . Field ( this ) ;
194229 }
195230
196- public static bool operator == ( Field x , Field y ) => Equals ( x , y ) ;
231+ #endregion IUrlParameter
232+
233+ #region Debugging
234+
235+ public override string ToString ( ) =>
236+ $ "{ Expression ? . ToString ( ) ?? PropertyDebug ?? Name } { ( Boost . HasValue ? "^" + Boost . Value : string . Empty ) } " +
237+ $ "{ ( ! string . IsNullOrEmpty ( Format ) ? " format: " + Format : string . Empty ) } " +
238+ $ "{ ( _type == null ? string . Empty : " typeof: " + _type . Name ) } ";
239+
240+ internal string DebuggerDisplay => ToString ( ) ;
241+
242+ private string ? PropertyDebug => Property is null ? null : $ "PropertyInfo: { Property . Name } ";
197243
198- public static bool operator != ( Field x , Field y ) => ! Equals ( x , y ) ;
244+ #endregion Debugging
245+
246+ [ return : NotNullIfNotNull ( nameof ( name ) ) ]
247+ private static string ? ParseFieldName ( string ? name , out double ? boost )
248+ {
249+ boost = null ;
250+ if ( name is null )
251+ return null ;
252+
253+ var caretIndex = name . IndexOf ( '^' ) ;
254+ if ( caretIndex == - 1 )
255+ return name ;
256+
257+ var parts = name . Split ( new [ ] { '^' } , 2 , StringSplitOptions . RemoveEmptyEntries ) ;
258+ name = parts [ 0 ] ;
259+ boost = double . Parse ( parts [ 1 ] , CultureInfo . InvariantCulture ) ;
260+ return name ;
261+ }
199262}
0 commit comments