-
Notifications
You must be signed in to change notification settings - Fork 820
/
IndexDefinitionHelper.cs
133 lines (120 loc) · 3.91 KB
/
IndexDefinitionHelper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using Raven.Client.Document;
namespace Raven.Client.Indexes
{
/// <summary>
/// Generate index defintion from linq expressions
/// </summary>
public class IndexDefinitionHelper
{
/// <summary>
/// Perform the actual generation
/// </summary>
public static string PruneToFailureLinqQueryAsStringToWorkableCode<TQueryRoot>(
LambdaExpression expr,
DocumentConvention convention,
string querySource, bool translateIdentityProperty)
{
if (expr == null)
return null;
var expression = expr.Body;
#if !NET_3_5
string queryRootName = null;
#endif
switch (expression.NodeType)
{
case ExpressionType.ConvertChecked:
case ExpressionType.Convert:
expression = ((UnaryExpression)expression).Operand;
break;
#if !NET_3_5
case ExpressionType.Call:
var methodCallExpression = ((MethodCallExpression)expression);
switch (methodCallExpression.Method.Name)
{
case "Select":
queryRootName = TryCaptureQueryRoot(methodCallExpression.Arguments[0]);
break;
case "SelectMany":
queryRootName = TryCaptureQueryRoot(methodCallExpression.Arguments[1]);
break;
}
break;
#endif
}
#if !NET_3_5
var linqQuery = ExpressionStringBuilder.ExpressionToString(convention, translateIdentityProperty, typeof(TQueryRoot), queryRootName, expression);
#else
var linqQuery =expression.ToString();
#endif
var querySourceName = expr.Parameters.First(x => x.Type != typeof(IClientSideDatabase)).Name;
if (linqQuery.StartsWith(querySourceName))
linqQuery = querySource + linqQuery.Substring(querySourceName.Length);
else if (linqQuery.StartsWith("(" + querySourceName + ")"))
linqQuery = querySource + linqQuery.Substring(querySourceName.Length + 2);
else
throw new InvalidOperationException("Canot understand how to parse the query");
linqQuery = ReplaceAnonymousTypeBraces(linqQuery);
linqQuery = Regex.Replace(linqQuery, @"new ((VB\$)|(<>))[\w_]+(`\d+)?", "new ");// remove anonymous types
linqQuery = Regex.Replace(linqQuery, @"<>([a-z])_", "__$1_"); // replace <>h_ in transperant identifiers
const string pattern = @"(\.Where\(|\.Select\(|\.GroupBy\(|\.SelectMany)";
linqQuery = Regex.Replace(linqQuery, pattern, "\r\n\t$1"); // formatting
return linqQuery;
}
private static string TryCaptureQueryRoot(Expression expression)
{
if (expression.NodeType != ExpressionType.Lambda)
return null;
var parameters = ((LambdaExpression)expression).Parameters;
if (parameters.Count != 1)
return null;
var parameterExpression = parameters[0];
return parameterExpression.Name;
}
private static string ReplaceAnonymousTypeBraces(string linqQuery)
{
const string pattern = @"new ((VB\$)|(<>))[\w_]+(`\d+)?";
var matches = Regex.Matches(linqQuery, pattern);
for (int i = 0; i < matches.Count; i++)
{
var match = matches[i];
int endBrace = -1;
var startBrace = linqQuery[match.Index + match.Length];
int startIndex = match.Index + match.Length;
if (startBrace != '(')
break;
int otherBraces = 0;
for (int j = startIndex + 1; j < linqQuery.Length; j++)
{
if (linqQuery[j] == '(')
{
otherBraces++;
continue;
}
else if (linqQuery[j] != ')')
continue;
if (otherBraces == 0)
{
endBrace = j;
break;
}
otherBraces--;
}
if (endBrace != -1)
{
string s = linqQuery.Substring(0, match.Index + match.Length) + "{";
s += linqQuery.Substring(startIndex + 1, endBrace - startIndex - 1) + "}";
s += linqQuery.Substring(endBrace + 1);
linqQuery = s;
matches = Regex.Matches(linqQuery, pattern);
continue;
}
break;
}
return linqQuery;
}
}
}