/
IndexDefinitionBuilder.cs
150 lines (135 loc) · 5.97 KB
/
IndexDefinitionBuilder.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//-----------------------------------------------------------------------
// <copyright file="IndexDefinition.cs" company="Hibernating Rhinos LTD">
// Copyright (c) Hibernating Rhinos LTD. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Raven.Abstractions.Indexing;
using Raven.Client.Document;
namespace Raven.Client.Indexes
{
/// <summary>
/// This class provides a way to define a strongly typed index on the client.
/// </summary>
public class IndexDefinitionBuilder<TDocument, TReduceResult>
{
/// <summary>
/// Gets or sets the map function
/// </summary>
/// <value>The map.</value>
public Expression<Func<IEnumerable<TDocument>, IEnumerable>> Map { get; set; }
/// <summary>
/// Gets or sets the reduce function
/// </summary>
/// <value>The reduce.</value>
public Expression<Func<IEnumerable<TReduceResult>, IEnumerable>> Reduce { get; set; }
/// <summary>
/// Gets or sets the reduce function
/// </summary>
/// <value>The reduce.</value>
public Expression<Func<IClientSideDatabase,IEnumerable<TReduceResult>, IEnumerable>> TransformResults { get; set; }
/// <summary>
/// Gets or sets the stores options
/// </summary>
/// <value>The stores.</value>
public IDictionary<Expression<Func<TReduceResult, object>>, FieldStorage> Stores { get; set; }
/// <summary>
/// Gets or sets the indexing options
/// </summary>
/// <value>The indexes.</value>
public IDictionary<Expression<Func<TReduceResult, object>>, FieldIndexing> Indexes { get; set; }
/// <summary>
/// Gets or sets the sort options.
/// </summary>
/// <value>The sort options.</value>
public IDictionary<Expression<Func<TReduceResult, object>>, SortOptions> SortOptions { get; set; }
/// <summary>
/// Get os set the analyzers
/// </summary>
public IDictionary<Expression<Func<TReduceResult, object>>, string> Analyzers { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="IndexDefinitionBuilder{TDocument,TReduceResult}"/> class.
/// </summary>
public IndexDefinitionBuilder()
{
Stores = new Dictionary<Expression<Func<TReduceResult, object>>, FieldStorage>();
Indexes = new Dictionary<Expression<Func<TReduceResult, object>>, FieldIndexing>();
SortOptions = new Dictionary<Expression<Func<TReduceResult, object>>, SortOptions>();
Analyzers = new Dictionary<Expression<Func<TReduceResult, object>>, string>();
}
/// <summary>
/// Toes the index definition.
/// </summary>
/// <param name="convention">The convention.</param>
/// <returns></returns>
public IndexDefinition ToIndexDefinition(DocumentConvention convention, bool validateMap = true)
{
if (Map == null && validateMap)
throw new InvalidOperationException(
string.Format("Map is required to generate an index, you cannot create an index without a valid Map property (in index {0}).", GetType().Name));
string querySource = (typeof(TDocument) == typeof(object) || ContainsWhereEntityIs(Map.Body)) ? "docs" : "docs." + convention.GetTypeTagName(typeof(TDocument));
var indexDefinition = new IndexDefinition
{
Reduce = IndexDefinitionHelper.PruneToFailureLinqQueryAsStringToWorkableCode<TDocument>(Reduce, convention, "results", translateIdentityProperty: false),
TransformResults = IndexDefinitionHelper.PruneToFailureLinqQueryAsStringToWorkableCode<TDocument>(TransformResults, convention, "results", translateIdentityProperty: true),
Indexes = ConvertToStringDictionary(Indexes),
Stores = ConvertToStringDictionary(Stores),
SortOptions = ConvertToStringDictionary(SortOptions),
Analyzers = ConvertToStringDictionary(Analyzers)
};
if (Map != null)
indexDefinition.Map = IndexDefinitionHelper.PruneToFailureLinqQueryAsStringToWorkableCode<TDocument>(Map, convention,
querySource,
translateIdentityProperty
: true);
return indexDefinition;
}
#if !NET_3_5
private static bool ContainsWhereEntityIs(Expression body)
{
var whereEntityIsVisitor = new WhereEntityIsVisitor();
whereEntityIsVisitor.Visit(body);
return whereEntityIsVisitor.HasWhereEntityIs;
}
private class WhereEntityIsVisitor : ExpressionVisitor
{
public bool HasWhereEntityIs { get; set; }
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.Name == "WhereEntityIs")
HasWhereEntityIs = true;
return base.VisitMethodCall(node);
}
}
#else
private static bool ContainsWhereEntityIs(Expression body)
{
return body.ToString().Contains("WhereEntityIs");
}
#endif
private static IDictionary<string, TValue> ConvertToStringDictionary<TValue>(IEnumerable<KeyValuePair<Expression<Func<TReduceResult, object>>, TValue>> input)
{
var result = new Dictionary<string, TValue>();
foreach (var value in input)
{
result[(GetMemberExpression(value.Key)).Member.Name] = value.Value;
}
return result;
}
private static MemberExpression GetMemberExpression(Expression<Func<TReduceResult, object>> value)
{
if(value.Body is UnaryExpression)
return (MemberExpression)((UnaryExpression) value.Body).Operand;
return (MemberExpression) value.Body;
}
}
/// <summary>
/// This class provides a way to define a strongly typed index on the client.
/// </summary>
public class IndexDefinitionBuilder<TDocument> : IndexDefinitionBuilder<TDocument, TDocument> { }
}