/src/Raven.Client/Documents/Linq/RavenQueryProvider.cs

https://github.com/fitzchak/ravendb · C# · 313 lines · 194 code · 39 blank · 80 comment · 6 complexity · 97012f750942046598c5608406b1d783 MD5 · raw file

  1. //-----------------------------------------------------------------------
  2. // <copyright file="RavenQueryProvider.cs" company="Hibernating Rhinos LTD">
  3. // Copyright (c) Hibernating Rhinos LTD. All rights reserved.
  4. // </copyright>
  5. //-----------------------------------------------------------------------
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Linq.Expressions;
  10. using System.Reflection;
  11. using System.Threading;
  12. using System.Threading.Tasks;
  13. using Raven.Client.Documents.Queries;
  14. using Raven.Client.Documents.Session;
  15. using Raven.Client.Extensions;
  16. namespace Raven.Client.Documents.Linq
  17. {
  18. /// <summary>
  19. /// An implementation of <see cref="IRavenQueryProvider"/>
  20. /// </summary>
  21. internal class RavenQueryProvider<T> : IRavenQueryProvider
  22. {
  23. private Action<QueryResult> _afterQueryExecuted;
  24. private Action<IDocumentQueryCustomization> _customizeQuery;
  25. private readonly string _indexName;
  26. private readonly string _collectionName;
  27. private readonly IDocumentQueryGenerator _queryGenerator;
  28. private readonly QueryStatistics _queryStatistics;
  29. #if FEATURE_HIGHLIGHTING
  30. private readonly QueryHighlightings _highlightings;
  31. #endif
  32. private readonly bool _isMapReduce;
  33. /// <summary>
  34. /// Initializes a new instance of the <see cref="RavenQueryProvider{T}"/> class.
  35. /// </summary>
  36. public RavenQueryProvider(
  37. IDocumentQueryGenerator queryGenerator,
  38. string indexName,
  39. string collectionName,
  40. Type originalQueryType,
  41. QueryStatistics queryStatistics,
  42. #if FEATURE_HIGHLIGHTING
  43. QueryHighlightings highlightings,
  44. #endif
  45. bool isMapReduce)
  46. {
  47. FieldsToFetch = new HashSet<FieldToFetch>();
  48. OriginalQueryType = originalQueryType;
  49. _queryGenerator = queryGenerator;
  50. _indexName = indexName;
  51. _collectionName = collectionName;
  52. _queryStatistics = queryStatistics;
  53. #if FEATURE_HIGHLIGHTING
  54. _highlightings = highlightings;
  55. #endif
  56. _isMapReduce = isMapReduce;
  57. }
  58. /// <summary>
  59. /// Gets the name of the index.
  60. /// </summary>
  61. /// <value>The name of the index.</value>
  62. public string IndexName => _indexName;
  63. /// <summary>
  64. /// Gets the name of the collection.
  65. /// </summary>
  66. /// <value>The name of the collection.</value>
  67. public string CollectionName => _collectionName;
  68. /// <summary>
  69. /// Get the query generator
  70. /// </summary>
  71. public IDocumentQueryGenerator QueryGenerator => _queryGenerator;
  72. /// <summary>
  73. /// Gets the actions for customizing the generated lucene query
  74. /// </summary>
  75. public Action<IDocumentQueryCustomization> CustomizeQuery => _customizeQuery;
  76. /// <summary>
  77. /// Set the fields to fetch
  78. /// </summary>
  79. public HashSet<FieldToFetch> FieldsToFetch { get; }
  80. public Type OriginalQueryType { get; }
  81. /// <summary>
  82. /// Change the result type for the query provider
  83. /// </summary>
  84. public IRavenQueryProvider For<TS>()
  85. {
  86. if (typeof(T) == typeof(TS))
  87. return this;
  88. var ravenQueryProvider = new RavenQueryProvider<TS>(
  89. _queryGenerator,
  90. _indexName,
  91. _collectionName,
  92. OriginalQueryType,
  93. _queryStatistics,
  94. #if FEATURE_HIGHLIGHTING
  95. _highlightings,
  96. #endif
  97. _isMapReduce);
  98. ravenQueryProvider.Customize(_customizeQuery);
  99. return ravenQueryProvider;
  100. }
  101. /// <summary>
  102. /// Executes the query represented by a specified expression tree.
  103. /// </summary>
  104. /// <param name="expression">An expression tree that represents a LINQ query.</param>
  105. /// <returns>
  106. /// The value that results from executing the specified query.
  107. /// </returns>
  108. public virtual object Execute(Expression expression)
  109. {
  110. return GetQueryProviderProcessor<T>().Execute(expression);
  111. }
  112. IQueryable<TS> IQueryProvider.CreateQuery<TS>(Expression expression)
  113. {
  114. var a = _queryGenerator.CreateRavenQueryInspector<TS>();
  115. a.Init(
  116. this,
  117. _queryStatistics,
  118. #if FEATURE_HIGHLIGHTING
  119. _highlightings,
  120. #endif
  121. _indexName,
  122. _collectionName,
  123. expression,
  124. (InMemoryDocumentSessionOperations)_queryGenerator,
  125. _isMapReduce);
  126. return a;
  127. }
  128. IQueryable IQueryProvider.CreateQuery(Expression expression)
  129. {
  130. var elementType = ReflectionExtensions.GetElementType(expression.Type);
  131. try
  132. {
  133. var queryInspectorGenericType = typeof(RavenQueryInspector<>).MakeGenericType(elementType);
  134. var args = new object[]
  135. {
  136. this,
  137. _queryStatistics,
  138. #if FEATURE_HIGHLIGHTING
  139. _highlightings,
  140. #endif
  141. _indexName,
  142. _collectionName,
  143. expression,
  144. _queryGenerator,
  145. _isMapReduce
  146. };
  147. var queryInspectorInstance = Activator.CreateInstance(queryInspectorGenericType);
  148. var methodInfo = queryInspectorGenericType.GetMethod("Init");
  149. methodInfo.Invoke(queryInspectorInstance, args);
  150. return (IQueryable)queryInspectorInstance;
  151. }
  152. catch (TargetInvocationException tie)
  153. {
  154. throw tie.InnerException;
  155. }
  156. }
  157. /// <summary>
  158. /// Executes the specified expression.
  159. /// </summary>
  160. /// <typeparam name="TS"></typeparam>
  161. /// <param name="expression">The expression.</param>
  162. /// <returns></returns>
  163. TS IQueryProvider.Execute<TS>(Expression expression)
  164. {
  165. return (TS)Execute(expression);
  166. }
  167. /// <summary>
  168. /// Executes the query represented by a specified expression tree.
  169. /// </summary>
  170. /// <param name="expression">An expression tree that represents a LINQ query.</param>
  171. /// <returns>
  172. /// The value that results from executing the specified query.
  173. /// </returns>
  174. object IQueryProvider.Execute(Expression expression)
  175. {
  176. return Execute(expression);
  177. }
  178. /// <summary>
  179. /// Callback to get the results of the query
  180. /// </summary>
  181. public void AfterQueryExecuted(Action<QueryResult> afterQueryExecutedCallback)
  182. {
  183. _afterQueryExecuted = afterQueryExecutedCallback;
  184. }
  185. public void InvokeAfterQueryExecuted(QueryResult result)
  186. {
  187. _afterQueryExecuted?.Invoke(result);
  188. }
  189. /// <summary>
  190. /// Customizes the query using the specified action
  191. /// </summary>
  192. /// <param name="action">The action.</param>
  193. public virtual void Customize(Action<IDocumentQueryCustomization> action)
  194. {
  195. if (action == null)
  196. return;
  197. _customizeQuery += action;
  198. }
  199. /// <summary>
  200. /// Convert the expression to a Lucene query
  201. /// </summary>
  202. public IAsyncDocumentQuery<TResult> ToAsyncDocumentQuery<TResult>(Expression expression)
  203. {
  204. var processor = GetQueryProviderProcessor<T>();
  205. var documentQuery = (IAsyncDocumentQuery<TResult>)processor.GetAsyncDocumentQueryFor(expression);
  206. return documentQuery;
  207. }
  208. /// <summary>
  209. /// Register the query as a lazy query in the session and return a lazy
  210. /// instance that will evaluate the query only when needed
  211. /// </summary>
  212. public Lazy<IEnumerable<TS>> Lazily<TS>(Expression expression, Action<IEnumerable<TS>> onEval)
  213. {
  214. var processor = GetQueryProviderProcessor<TS>();
  215. var query = processor.GetDocumentQueryFor(expression);
  216. if (FieldsToFetch.Count > 0)
  217. {
  218. var (fields, projections) = processor.GetProjections();
  219. query = query.SelectFields<TS>(new QueryData(fields, projections));
  220. }
  221. return query.Lazily(onEval);
  222. }
  223. /// <summary>
  224. /// Register the query as a lazy async query in the session and return a lazy async
  225. /// instance that will evaluate the query only when needed
  226. /// </summary>
  227. public Lazy<Task<IEnumerable<TS>>> LazilyAsync<TS>(Expression expression, Action<IEnumerable<TS>> onEval)
  228. {
  229. var processor = GetQueryProviderProcessor<TS>();
  230. var query = processor.GetAsyncDocumentQueryFor(expression);
  231. if (FieldsToFetch.Count > 0)
  232. {
  233. var (fields, projections) = processor.GetProjections();
  234. query = query.SelectFields<TS>(new QueryData(fields, projections));
  235. }
  236. return query.LazilyAsync(onEval);
  237. }
  238. /// <summary>
  239. /// Register the query as a lazy-count query in the session and return a lazy
  240. /// instance that will evaluate the query only when needed
  241. /// </summary>
  242. public Lazy<int> CountLazily<TS>(Expression expression)
  243. {
  244. var processor = GetQueryProviderProcessor<TS>();
  245. var query = processor.GetDocumentQueryFor(expression);
  246. return query.CountLazily();
  247. }
  248. /// <summary>
  249. /// Register the query as a lazy-count query in the session and return a lazy
  250. /// instance that will evaluate the query only when needed
  251. /// </summary>
  252. public Lazy<Task<int>> CountLazilyAsync<TS>(Expression expression, CancellationToken token = default(CancellationToken))
  253. {
  254. var processor = GetQueryProviderProcessor<TS>();
  255. var query = processor.GetAsyncDocumentQueryFor(expression);
  256. return query.CountLazilyAsync(token);
  257. }
  258. protected virtual RavenQueryProviderProcessor<TS> GetQueryProviderProcessor<TS>()
  259. {
  260. return new RavenQueryProviderProcessor<TS>(_queryGenerator, _customizeQuery, _afterQueryExecuted, _indexName, _collectionName,
  261. FieldsToFetch,
  262. _isMapReduce, OriginalQueryType);
  263. }
  264. /// <summary>
  265. /// Convert the expression to a Lucene query
  266. /// </summary>
  267. public IDocumentQuery<TResult> ToDocumentQuery<TResult>(Expression expression)
  268. {
  269. var processor = GetQueryProviderProcessor<T>();
  270. var result = (IDocumentQuery<TResult>)processor.GetDocumentQueryFor(expression);
  271. return result;
  272. }
  273. }
  274. }