@@ -806,35 +806,103 @@ private IMongoQuery BuildStringLengthQuery(Expression variableExpression, Expres
806
806
807
807
private IMongoQuery BuildStringQuery ( MethodCallExpression methodCallExpression )
808
808
{
809
- if ( methodCallExpression . Method . DeclaringType = = typeof ( string ) )
809
+ if ( methodCallExpression . Method . DeclaringType ! = typeof ( string ) )
810
810
{
811
- switch ( methodCallExpression . Method . Name )
811
+ return null ;
812
+ }
813
+
814
+ var arguments = methodCallExpression . Arguments . ToArray ( ) ;
815
+ if ( arguments . Length != 1 )
816
+ {
817
+ return null ;
818
+ }
819
+
820
+ var stringExpression = methodCallExpression . Object ;
821
+ var constantExpression = arguments [ 0 ] as ConstantExpression ;
822
+ if ( constantExpression == null )
823
+ {
824
+ return null ;
825
+ }
826
+
827
+ var pattern = ( string ) constantExpression . Value ; // TODO: escape value
828
+ switch ( methodCallExpression . Method . Name )
829
+ {
830
+ case "Contains" : pattern = ".*" + pattern + ".*" ; break ;
831
+ case "EndsWith" : pattern = ".*" + pattern ; break ;
832
+ case "StartsWith" : pattern = pattern + ".*" ; break ;
833
+ default : return null ;
834
+ }
835
+
836
+ var caseInsensitive = false ;
837
+ MethodCallExpression stringMethodCallExpression ;
838
+ while ( ( stringMethodCallExpression = stringExpression as MethodCallExpression ) != null )
839
+ {
840
+ var trimStart = false ;
841
+ var trimEnd = false ;
842
+ Expression trimCharsExpression = null ;
843
+ switch ( stringMethodCallExpression . Method . Name )
812
844
{
813
- case "Contains" :
814
- case "EndsWith" :
815
- case "StartsWith" :
816
- var arguments = methodCallExpression . Arguments . ToArray ( ) ;
817
- if ( arguments . Length == 1 )
818
- {
819
- var serializationInfo = GetSerializationInfo ( methodCallExpression . Object ) ;
820
- var valueExpression = arguments [ 0 ] as ConstantExpression ;
821
- if ( serializationInfo != null && valueExpression != null )
822
- {
823
- var s = ( string ) valueExpression . Value ;
824
- BsonRegularExpression regex ;
825
- switch ( methodCallExpression . Method . Name )
826
- {
827
- case "Contains" : regex = new BsonRegularExpression ( s ) ; break ;
828
- case "EndsWith" : regex = new BsonRegularExpression ( s + "$" ) ; break ;
829
- case "StartsWith" : regex = new BsonRegularExpression ( "^" + s ) ; break ;
830
- default : throw new InvalidOperationException ( "Unreachable code" ) ;
831
- }
832
- return Query . Matches ( serializationInfo . ElementName , regex ) ;
833
- }
834
- }
845
+ case "ToLower" :
846
+ caseInsensitive = true ;
847
+ break ;
848
+ case "ToUpper" :
849
+ caseInsensitive = true ;
835
850
break ;
851
+ case "Trim" :
852
+ trimStart = true ;
853
+ trimEnd = true ;
854
+ trimCharsExpression = stringMethodCallExpression . Arguments . FirstOrDefault ( ) ;
855
+ break ;
856
+ case "TrimEnd" :
857
+ trimEnd = true ;
858
+ trimCharsExpression = stringMethodCallExpression . Arguments . First ( ) ;
859
+ break ;
860
+ case "TrimStart" :
861
+ trimStart = true ;
862
+ trimCharsExpression = stringMethodCallExpression . Arguments . First ( ) ;
863
+ break ;
864
+ default :
865
+ return null ;
836
866
}
867
+
868
+ if ( trimStart || trimEnd )
869
+ {
870
+ var trimCharsPattern = GetTrimCharsPattern ( trimCharsExpression ) ;
871
+ if ( trimCharsPattern == null )
872
+ {
873
+ return null ;
874
+ }
875
+
876
+ if ( trimStart )
877
+ {
878
+ pattern = trimCharsPattern + pattern ;
879
+ }
880
+ if ( trimEnd )
881
+ {
882
+ pattern = pattern + trimCharsPattern ;
883
+ }
884
+ }
885
+
886
+ stringExpression = stringMethodCallExpression . Object ;
887
+ }
888
+
889
+ pattern = "^" + pattern + "$" ;
890
+ if ( pattern . StartsWith ( "^.*" ) )
891
+ {
892
+ pattern = pattern . Substring ( 3 ) ;
893
+ }
894
+ if ( pattern . EndsWith ( ".*$" ) )
895
+ {
896
+ pattern = pattern . Substring ( 0 , pattern . Length - 3 ) ;
837
897
}
898
+
899
+ var serializationInfo = GetSerializationInfo ( stringExpression ) ;
900
+ if ( serializationInfo != null )
901
+ {
902
+ var options = caseInsensitive ? "is" : "s" ;
903
+ return Query . Matches ( serializationInfo . ElementName , new BsonRegularExpression ( pattern , options ) ) ;
904
+ }
905
+
838
906
return null ;
839
907
}
840
908
@@ -1004,6 +1072,49 @@ private BsonSerializationInfo GetSerializationInfoMember(IBsonSerializer seriali
1004
1072
}
1005
1073
}
1006
1074
1075
+ private string GetTrimCharsPattern ( Expression trimCharsExpression )
1076
+ {
1077
+ if ( trimCharsExpression == null )
1078
+ {
1079
+ return "\\ s*" ;
1080
+ }
1081
+
1082
+ var constantExpresion = trimCharsExpression as ConstantExpression ;
1083
+ if ( constantExpresion == null || constantExpresion . Type != typeof ( char [ ] ) )
1084
+ {
1085
+ return null ;
1086
+ }
1087
+
1088
+ var trimChars = ( char [ ] ) constantExpresion . Value ;
1089
+ if ( trimChars . Length == 0 )
1090
+ {
1091
+ return "\\ s*" ;
1092
+ }
1093
+
1094
+ // build a pattern that matches the characters to be trimmed
1095
+ var sb = new StringBuilder ( ) ;
1096
+ sb . Append ( "[" ) ;
1097
+ var sawDash = false ; // if dash is one of the characters it must be last in the pattern
1098
+ foreach ( var c in trimChars )
1099
+ {
1100
+ if ( c == '-' )
1101
+ {
1102
+ sawDash = true ;
1103
+ }
1104
+ else
1105
+ {
1106
+ // TODO: handle special characters better
1107
+ sb . Append ( c . ToString ( ) ) ;
1108
+ }
1109
+ }
1110
+ if ( sawDash )
1111
+ {
1112
+ sb . Append ( "-" ) ;
1113
+ }
1114
+ sb . Append ( "]*" ) ;
1115
+ return sb . ToString ( ) ;
1116
+ }
1117
+
1007
1118
private BsonValue SerializeValue ( BsonSerializationInfo serializationInfo , object value )
1008
1119
{
1009
1120
var bsonDocument = new BsonDocument ( ) ;
0 commit comments