IAggregateFluentExtensions.cs 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. /* Copyright 2010-present MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Linq;
  18. using System.Linq.Expressions;
  19. using System.Threading;
  20. using System.Threading.Tasks;
  21. using MongoDB.Bson;
  22. using MongoDB.Bson.Serialization;
  23. using MongoDB.Driver.Core.Misc;
  24. namespace MongoDB.Driver
  25. {
  26. /// <summary>
  27. /// Extension methods for <see cref="IAggregateFluent{TResult}"/>
  28. /// </summary>
  29. public static class IAggregateFluentExtensions
  30. {
  31. /// <summary>
  32. /// Appends a $bucket stage to the pipeline.
  33. /// </summary>
  34. /// <typeparam name="TResult">The type of the result.</typeparam>
  35. /// <typeparam name="TValue">The type of the value.</typeparam>
  36. /// <param name="aggregate">The aggregate.</param>
  37. /// <param name="groupBy">The expression providing the value to group by.</param>
  38. /// <param name="boundaries">The bucket boundaries.</param>
  39. /// <param name="options">The options.</param>
  40. /// <returns>The fluent aggregate interface.</returns>
  41. public static IAggregateFluent<AggregateBucketResult<TValue>> Bucket<TResult, TValue>(
  42. this IAggregateFluent<TResult> aggregate,
  43. Expression<Func<TResult, TValue>> groupBy,
  44. IEnumerable<TValue> boundaries,
  45. AggregateBucketOptions<TValue> options = null)
  46. {
  47. Ensure.IsNotNull(aggregate, nameof(aggregate));
  48. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Bucket(groupBy, boundaries, options));
  49. }
  50. /// <summary>
  51. /// Appends a $bucket stage to the pipeline.
  52. /// </summary>
  53. /// <typeparam name="TResult">The type of the result.</typeparam>
  54. /// <typeparam name="TValue">The type of the value.</typeparam>
  55. /// <typeparam name="TNewResult">The type of the new result.</typeparam>
  56. /// <param name="aggregate">The aggregate.</param>
  57. /// <param name="groupBy">The expression providing the value to group by.</param>
  58. /// <param name="boundaries">The bucket boundaries.</param>
  59. /// <param name="output">The output projection.</param>
  60. /// <param name="options">The options.</param>
  61. /// <returns>The fluent aggregate interface.</returns>
  62. public static IAggregateFluent<TNewResult> Bucket<TResult, TValue, TNewResult>(
  63. this IAggregateFluent<TResult> aggregate,
  64. Expression<Func<TResult, TValue>> groupBy,
  65. IEnumerable<TValue> boundaries,
  66. Expression<Func<IGrouping<TValue, TResult>, TNewResult>> output,
  67. AggregateBucketOptions<TValue> options = null)
  68. {
  69. Ensure.IsNotNull(aggregate, nameof(aggregate));
  70. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Bucket(groupBy, boundaries, output, options));
  71. }
  72. /// <summary>
  73. /// Appends a $bucketAuto stage to the pipeline.
  74. /// </summary>
  75. /// <typeparam name="TResult">The type of the result.</typeparam>
  76. /// <typeparam name="TValue">The type of the value.</typeparam>
  77. /// <param name="aggregate">The aggregate.</param>
  78. /// <param name="groupBy">The expression providing the value to group by.</param>
  79. /// <param name="buckets">The number of buckets.</param>
  80. /// <param name="options">The options (optional).</param>
  81. /// <returns>The fluent aggregate interface.</returns>
  82. public static IAggregateFluent<AggregateBucketAutoResult<TValue>> BucketAuto<TResult, TValue>(
  83. this IAggregateFluent<TResult> aggregate,
  84. Expression<Func<TResult, TValue>> groupBy,
  85. int buckets,
  86. AggregateBucketAutoOptions options = null)
  87. {
  88. Ensure.IsNotNull(aggregate, nameof(aggregate));
  89. return aggregate.AppendStage(PipelineStageDefinitionBuilder.BucketAuto(groupBy, buckets, options));
  90. }
  91. /// <summary>
  92. /// Appends a $bucketAuto stage to the pipeline.
  93. /// </summary>
  94. /// <typeparam name="TResult">The type of the result.</typeparam>
  95. /// <typeparam name="TValue">The type of the value.</typeparam>
  96. /// <typeparam name="TNewResult">The type of the new result.</typeparam>
  97. /// <param name="aggregate">The aggregate.</param>
  98. /// <param name="groupBy">The expression providing the value to group by.</param>
  99. /// <param name="buckets">The number of buckets.</param>
  100. /// <param name="output">The output projection.</param>
  101. /// <param name="options">The options (optional).</param>
  102. /// <returns>The fluent aggregate interface.</returns>
  103. public static IAggregateFluent<TNewResult> BucketAuto<TResult, TValue, TNewResult>(
  104. this IAggregateFluent<TResult> aggregate,
  105. Expression<Func<TResult, TValue>> groupBy,
  106. int buckets,
  107. Expression<Func<IGrouping<TValue, TResult>, TNewResult>> output,
  108. AggregateBucketAutoOptions options = null)
  109. {
  110. Ensure.IsNotNull(aggregate, nameof(aggregate));
  111. return aggregate.AppendStage(PipelineStageDefinitionBuilder.BucketAuto(groupBy, buckets, output, options));
  112. }
  113. /// <summary>
  114. /// Appends a $facet stage to the pipeline.
  115. /// </summary>
  116. /// <typeparam name="TResult">The type of the result.</typeparam>
  117. /// <param name="aggregate">The aggregate.</param>
  118. /// <param name="facets">The facets.</param>
  119. /// <returns>The fluent aggregate interface.</returns>
  120. public static IAggregateFluent<AggregateFacetResults> Facet<TResult>(
  121. this IAggregateFluent<TResult> aggregate,
  122. IEnumerable<AggregateFacet<TResult>> facets)
  123. {
  124. Ensure.IsNotNull(aggregate, nameof(aggregate));
  125. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Facet(facets));
  126. }
  127. /// <summary>
  128. /// Appends a $facet stage to the pipeline.
  129. /// </summary>
  130. /// <typeparam name="TResult">The type of the result.</typeparam>
  131. /// <param name="aggregate">The aggregate.</param>
  132. /// <param name="facets">The facets.</param>
  133. /// <returns>The fluent aggregate interface.</returns>
  134. public static IAggregateFluent<AggregateFacetResults> Facet<TResult>(
  135. this IAggregateFluent<TResult> aggregate,
  136. params AggregateFacet<TResult>[] facets)
  137. {
  138. Ensure.IsNotNull(aggregate, nameof(aggregate));
  139. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Facet(facets));
  140. }
  141. /// <summary>
  142. /// Appends a $facet stage to the pipeline.
  143. /// </summary>
  144. /// <typeparam name="TResult">The type of the result.</typeparam>
  145. /// <typeparam name="TNewResult">The type of the new result.</typeparam>
  146. /// <param name="aggregate">The aggregate.</param>
  147. /// <param name="facets">The facets.</param>
  148. /// <returns>
  149. /// The fluent aggregate interface.
  150. /// </returns>
  151. public static IAggregateFluent<TNewResult> Facet<TResult, TNewResult>(
  152. this IAggregateFluent<TResult> aggregate,
  153. params AggregateFacet<TResult>[] facets)
  154. {
  155. Ensure.IsNotNull(aggregate, nameof(aggregate));
  156. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Facet<TResult, TNewResult>(facets));
  157. }
  158. /// <summary>
  159. /// Appends a $graphLookup stage to the pipeline.
  160. /// </summary>
  161. /// <typeparam name="TResult">The type of the result.</typeparam>
  162. /// <typeparam name="TFrom">The type of the from documents.</typeparam>
  163. /// <typeparam name="TConnectFrom">The type of the connect from field (must be either TConnectTo or a type that implements IEnumerable{TConnectTo}).</typeparam>
  164. /// <typeparam name="TConnectTo">The type of the connect to field.</typeparam>
  165. /// <typeparam name="TStartWith">The type of the start with expression (must be either TConnectTo or a type that implements IEnumerable{TConnectTo}).</typeparam>
  166. /// <typeparam name="TAs">The type of the as field.</typeparam>
  167. /// <typeparam name="TNewResult">The type of the new result (must be same as TResult with an additional as field).</typeparam>
  168. /// <param name="aggregate">The aggregate.</param>
  169. /// <param name="from">The from collection.</param>
  170. /// <param name="connectFromField">The connect from field.</param>
  171. /// <param name="connectToField">The connect to field.</param>
  172. /// <param name="startWith">The start with value.</param>
  173. /// <param name="as">The as field.</param>
  174. /// <param name="options">The options.</param>
  175. /// <returns>The fluent aggregate interface.</returns>
  176. public static IAggregateFluent<TNewResult> GraphLookup<TResult, TFrom, TConnectFrom, TConnectTo, TStartWith, TAs, TNewResult>(
  177. this IAggregateFluent<TResult> aggregate,
  178. IMongoCollection<TFrom> from,
  179. FieldDefinition<TFrom, TConnectFrom> connectFromField,
  180. FieldDefinition<TFrom, TConnectTo> connectToField,
  181. AggregateExpressionDefinition<TResult, TStartWith> startWith,
  182. FieldDefinition<TNewResult, TAs> @as,
  183. AggregateGraphLookupOptions<TFrom, TFrom, TNewResult> options = null)
  184. where TAs : IEnumerable<TFrom>
  185. {
  186. Ensure.IsNotNull(aggregate, nameof(aggregate));
  187. return aggregate.AppendStage(PipelineStageDefinitionBuilder.GraphLookup(from, connectFromField, connectToField, startWith, @as, options));
  188. }
  189. /// <summary>
  190. /// Appends a $graphLookup stage to the pipeline.
  191. /// </summary>
  192. /// <typeparam name="TResult">The type of the result.</typeparam>
  193. /// <typeparam name="TFrom">The type of the from documents.</typeparam>
  194. /// <param name="aggregate">The aggregate.</param>
  195. /// <param name="from">The from collection.</param>
  196. /// <param name="connectFromField">The connect from field.</param>
  197. /// <param name="connectToField">The connect to field.</param>
  198. /// <param name="startWith">The start with value.</param>
  199. /// <param name="as">The as field.</param>
  200. /// <param name="depthField">The depth field.</param>
  201. /// <returns>The fluent aggregate interface.</returns>
  202. public static IAggregateFluent<BsonDocument> GraphLookup<TResult, TFrom>(
  203. this IAggregateFluent<TResult> aggregate,
  204. IMongoCollection<TFrom> from,
  205. FieldDefinition<TFrom, BsonValue> connectFromField,
  206. FieldDefinition<TFrom, BsonValue> connectToField,
  207. AggregateExpressionDefinition<TResult, BsonValue> startWith,
  208. FieldDefinition<BsonDocument, IEnumerable<BsonDocument>> @as,
  209. FieldDefinition<BsonDocument, int> depthField = null)
  210. {
  211. Ensure.IsNotNull(aggregate, nameof(aggregate));
  212. return aggregate.AppendStage(PipelineStageDefinitionBuilder.GraphLookup(from, connectFromField, connectToField, startWith, @as, depthField));
  213. }
  214. /// <summary>
  215. /// Appends a $graphLookup stage to the pipeline.
  216. /// </summary>
  217. /// <typeparam name="TResult">The type of the result.</typeparam>
  218. /// <typeparam name="TNewResult">The type of the new result (must be same as TResult with an additional as field).</typeparam>
  219. /// <typeparam name="TFrom">The type of the from documents.</typeparam>
  220. /// <typeparam name="TConnectFrom">The type of the connect from field (must be either TConnectTo or a type that implements IEnumerable{TConnectTo}).</typeparam>
  221. /// <typeparam name="TConnectTo">The type of the connect to field.</typeparam>
  222. /// <typeparam name="TStartWith">The type of the start with expression (must be either TConnectTo or a type that implements IEnumerable{TConnectTo}).</typeparam>
  223. /// <typeparam name="TAs">The type of the as field.</typeparam>
  224. /// <param name="aggregate">The aggregate.</param>
  225. /// <param name="from">The from collection.</param>
  226. /// <param name="connectFromField">The connect from field.</param>
  227. /// <param name="connectToField">The connect to field.</param>
  228. /// <param name="startWith">The start with value.</param>
  229. /// <param name="as">The as field.</param>
  230. /// <param name="options">The options.</param>
  231. /// <returns>The fluent aggregate interface.</returns>
  232. public static IAggregateFluent<TNewResult> GraphLookup<TResult, TFrom, TConnectFrom, TConnectTo, TStartWith, TAs, TNewResult>(
  233. this IAggregateFluent<TResult> aggregate,
  234. IMongoCollection<TFrom> from,
  235. Expression<Func<TFrom, TConnectFrom>> connectFromField,
  236. Expression<Func<TFrom, TConnectTo>> connectToField,
  237. Expression<Func<TResult, TStartWith>> startWith,
  238. Expression<Func<TNewResult, TAs>> @as,
  239. AggregateGraphLookupOptions<TFrom, TFrom, TNewResult> options = null)
  240. where TAs : IEnumerable<TFrom>
  241. {
  242. Ensure.IsNotNull(aggregate, nameof(aggregate));
  243. return aggregate.AppendStage(PipelineStageDefinitionBuilder.GraphLookup(from, connectFromField, connectToField, startWith, @as, options, aggregate.Options?.TranslationOptions));
  244. }
  245. /// <summary>
  246. /// Appends a $graphLookup stage to the pipeline.
  247. /// </summary>
  248. /// <typeparam name="TResult">The type of the result.</typeparam>
  249. /// <typeparam name="TFrom">The type of the from documents.</typeparam>
  250. /// <typeparam name="TConnectFrom">The type of the connect from field (must be either TConnectTo or a type that implements IEnumerable{TConnectTo}).</typeparam>
  251. /// <typeparam name="TConnectTo">The type of the connect to field.</typeparam>
  252. /// <typeparam name="TStartWith">The type of the start with expression (must be either TConnectTo or a type that implements IEnumerable{TConnectTo}).</typeparam>
  253. /// <typeparam name="TAsElement">The type of the as field elements.</typeparam>
  254. /// <typeparam name="TAs">The type of the as field.</typeparam>
  255. /// <typeparam name="TNewResult">The type of the new result (must be same as TResult with an additional as field).</typeparam>
  256. /// <param name="aggregate">The aggregate.</param>
  257. /// <param name="from">The from collection.</param>
  258. /// <param name="connectFromField">The connect from field.</param>
  259. /// <param name="connectToField">The connect to field.</param>
  260. /// <param name="startWith">The start with value.</param>
  261. /// <param name="as">The as field.</param>
  262. /// <param name="depthField">The depth field.</param>
  263. /// <param name="options">The options.</param>
  264. /// <returns>The fluent aggregate interface.</returns>
  265. public static IAggregateFluent<TNewResult> GraphLookup<TResult, TFrom, TConnectFrom, TConnectTo, TStartWith, TAsElement, TAs, TNewResult>(
  266. this IAggregateFluent<TResult> aggregate,
  267. IMongoCollection<TFrom> from,
  268. Expression<Func<TFrom, TConnectFrom>> connectFromField,
  269. Expression<Func<TFrom, TConnectTo>> connectToField,
  270. Expression<Func<TResult, TStartWith>> startWith,
  271. Expression<Func<TNewResult, TAs>> @as,
  272. Expression<Func<TAsElement, int>> depthField,
  273. AggregateGraphLookupOptions<TFrom, TAsElement, TNewResult> options = null)
  274. where TAs : IEnumerable<TAsElement>
  275. {
  276. Ensure.IsNotNull(aggregate, nameof(aggregate));
  277. return aggregate.AppendStage(PipelineStageDefinitionBuilder.GraphLookup(from, connectFromField, connectToField, startWith, @as, depthField, options, aggregate.Options?.TranslationOptions));
  278. }
  279. /// <summary>
  280. /// Appends a group stage to the pipeline.
  281. /// </summary>
  282. /// <typeparam name="TResult">The type of the result.</typeparam>
  283. /// <param name="aggregate">The aggregate.</param>
  284. /// <param name="group">The group projection.</param>
  285. /// <returns>
  286. /// The fluent aggregate interface.
  287. /// </returns>
  288. public static IAggregateFluent<BsonDocument> Group<TResult>(this IAggregateFluent<TResult> aggregate, ProjectionDefinition<TResult, BsonDocument> group)
  289. {
  290. Ensure.IsNotNull(aggregate, nameof(aggregate));
  291. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Group(group));
  292. }
  293. /// <summary>
  294. /// Appends a group stage to the pipeline.
  295. /// </summary>
  296. /// <typeparam name="TResult">The type of the result.</typeparam>
  297. /// <typeparam name="TKey">The type of the key.</typeparam>
  298. /// <typeparam name="TNewResult">The type of the new result.</typeparam>
  299. /// <param name="aggregate">The aggregate.</param>
  300. /// <param name="id">The id.</param>
  301. /// <param name="group">The group projection.</param>
  302. /// <returns>
  303. /// The fluent aggregate interface.
  304. /// </returns>
  305. public static IAggregateFluent<TNewResult> Group<TResult, TKey, TNewResult>(this IAggregateFluent<TResult> aggregate, Expression<Func<TResult, TKey>> id, Expression<Func<IGrouping<TKey, TResult>, TNewResult>> group)
  306. {
  307. Ensure.IsNotNull(aggregate, nameof(aggregate));
  308. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Group(id, group));
  309. }
  310. /// <summary>
  311. /// Appends a lookup stage to the pipeline.
  312. /// </summary>
  313. /// <typeparam name="TResult">The type of the result.</typeparam>
  314. /// <param name="aggregate">The aggregate.</param>
  315. /// <param name="foreignCollectionName">Name of the foreign collection.</param>
  316. /// <param name="localField">The local field.</param>
  317. /// <param name="foreignField">The foreign field.</param>
  318. /// <param name="as">The field in the result to place the foreign matches.</param>
  319. /// <returns>The fluent aggregate interface.</returns>
  320. public static IAggregateFluent<BsonDocument> Lookup<TResult>(
  321. this IAggregateFluent<TResult> aggregate,
  322. string foreignCollectionName,
  323. FieldDefinition<TResult> localField,
  324. FieldDefinition<BsonDocument> foreignField,
  325. FieldDefinition<BsonDocument> @as)
  326. {
  327. Ensure.IsNotNull(aggregate, nameof(aggregate));
  328. Ensure.IsNotNull(foreignCollectionName, nameof(foreignCollectionName));
  329. var foreignCollection = aggregate.Database.GetCollection<BsonDocument>(foreignCollectionName);
  330. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Lookup(foreignCollection, localField, foreignField, @as));
  331. }
  332. /// <summary>
  333. /// Appends a lookup stage to the pipeline.
  334. /// </summary>
  335. /// <typeparam name="TResult">The type of the result.</typeparam>
  336. /// <typeparam name="TForeignDocument">The type of the foreign collection.</typeparam>
  337. /// <typeparam name="TNewResult">The type of the new result.</typeparam>
  338. /// <param name="aggregate">The aggregate.</param>
  339. /// <param name="foreignCollection">The foreign collection.</param>
  340. /// <param name="localField">The local field.</param>
  341. /// <param name="foreignField">The foreign field.</param>
  342. /// <param name="as">The field in the result to place the foreign matches.</param>
  343. /// <param name="options">The options.</param>
  344. /// <returns>The fluent aggregate interface.</returns>
  345. public static IAggregateFluent<TNewResult> Lookup<TResult, TForeignDocument, TNewResult>(
  346. this IAggregateFluent<TResult> aggregate,
  347. IMongoCollection<TForeignDocument> foreignCollection,
  348. Expression<Func<TResult, object>> localField,
  349. Expression<Func<TForeignDocument, object>> foreignField,
  350. Expression<Func<TNewResult, object>> @as,
  351. AggregateLookupOptions<TForeignDocument, TNewResult> options = null)
  352. {
  353. Ensure.IsNotNull(aggregate, nameof(aggregate));
  354. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Lookup(foreignCollection, localField, foreignField, @as, options));
  355. }
  356. /// <summary>
  357. /// Appends a lookup stage to the pipeline.
  358. /// </summary>
  359. /// <typeparam name="TResult">The type of the result.</typeparam>
  360. /// <param name="aggregate">The aggregate.</param>
  361. /// <param name="foreignCollection">The foreign collection.</param>
  362. /// <param name="let">The "let" definition.</param>
  363. /// <param name="lookupPipeline">The lookup pipeline.</param>
  364. /// <param name="as">The as field in the result in which to place the results of the lookup pipeline.</param>
  365. /// <returns>The fluent aggregate interface.</returns>
  366. public static IAggregateFluent<BsonDocument> Lookup<TResult>(
  367. this IAggregateFluent<TResult> aggregate,
  368. IMongoCollection<BsonDocument> foreignCollection,
  369. BsonDocument let,
  370. PipelineDefinition<BsonDocument, BsonDocument> lookupPipeline,
  371. FieldDefinition<BsonDocument, IEnumerable<BsonDocument>> @as)
  372. {
  373. Ensure.IsNotNull(aggregate, nameof(aggregate));
  374. Ensure.IsNotNull(foreignCollection, nameof(foreignCollection));
  375. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Lookup<TResult, BsonDocument, BsonDocument, IEnumerable<BsonDocument>, BsonDocument>(
  376. foreignCollection,
  377. let,
  378. lookupPipeline,
  379. @as));
  380. }
  381. /// <summary>
  382. /// Appends a lookup stage to the pipeline.
  383. /// </summary>
  384. /// <typeparam name="TResult">The type of the result.</typeparam>
  385. /// <typeparam name="TForeignDocument">The type of the foreign collection documents.</typeparam>
  386. /// <typeparam name="TAsElement">The type of the as field elements.</typeparam>
  387. /// <typeparam name="TAs">The type of the as field.</typeparam>
  388. /// <typeparam name="TNewResult">The type of the new result.</typeparam>
  389. /// <param name="aggregate">The aggregate.</param>
  390. /// <param name="foreignCollection">The foreign collection.</param>
  391. /// <param name="let">The "let" definition.</param>
  392. /// <param name="lookupPipeline">The lookup pipeline.</param>
  393. /// <param name="as">The as field in <typeparamref name="TNewResult" /> in which to place the results of the lookup pipeline.</param>
  394. /// <param name="options">The options.</param>
  395. /// <returns>The fluent aggregate interface.</returns>
  396. public static IAggregateFluent<TNewResult> Lookup<TResult, TForeignDocument, TAsElement, TAs, TNewResult>(
  397. this IAggregateFluent<TResult> aggregate,
  398. IMongoCollection<TForeignDocument> foreignCollection,
  399. BsonDocument let,
  400. PipelineDefinition<TForeignDocument, TAsElement> lookupPipeline,
  401. Expression<Func<TNewResult, TAs>> @as,
  402. AggregateLookupOptions<TForeignDocument, TNewResult> options = null)
  403. where TAs : IEnumerable<TAsElement>
  404. {
  405. Ensure.IsNotNull(aggregate, nameof(aggregate));
  406. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Lookup<TResult, TForeignDocument, TAsElement, TAs, TNewResult>(
  407. foreignCollection,
  408. let,
  409. lookupPipeline,
  410. @as,
  411. options));
  412. }
  413. /// <summary>
  414. /// Appends a match stage to the pipeline.
  415. /// </summary>
  416. /// <typeparam name="TResult">The type of the result.</typeparam>
  417. /// <param name="aggregate">The aggregate.</param>
  418. /// <param name="filter">The filter.</param>
  419. /// <returns>
  420. /// The fluent aggregate interface.
  421. /// </returns>
  422. public static IAggregateFluent<TResult> Match<TResult>(this IAggregateFluent<TResult> aggregate, Expression<Func<TResult, bool>> filter)
  423. {
  424. Ensure.IsNotNull(aggregate, nameof(aggregate));
  425. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Match(filter));
  426. }
  427. /// <summary>
  428. /// Appends a project stage to the pipeline.
  429. /// </summary>
  430. /// <typeparam name="TResult">The type of the result.</typeparam>
  431. /// <param name="aggregate">The aggregate.</param>
  432. /// <param name="projection">The projection.</param>
  433. /// <returns>
  434. /// The fluent aggregate interface.
  435. /// </returns>
  436. public static IAggregateFluent<BsonDocument> Project<TResult>(this IAggregateFluent<TResult> aggregate, ProjectionDefinition<TResult, BsonDocument> projection)
  437. {
  438. Ensure.IsNotNull(aggregate, nameof(aggregate));
  439. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Project(projection));
  440. }
  441. /// <summary>
  442. /// Appends a project stage to the pipeline.
  443. /// </summary>
  444. /// <typeparam name="TResult">The type of the result.</typeparam>
  445. /// <typeparam name="TNewResult">The type of the new result.</typeparam>
  446. /// <param name="aggregate">The aggregate.</param>
  447. /// <param name="projection">The projection.</param>
  448. /// <returns>
  449. /// The fluent aggregate interface.
  450. /// </returns>
  451. public static IAggregateFluent<TNewResult> Project<TResult, TNewResult>(this IAggregateFluent<TResult> aggregate, Expression<Func<TResult, TNewResult>> projection)
  452. {
  453. Ensure.IsNotNull(aggregate, nameof(aggregate));
  454. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Project(projection));
  455. }
  456. /// <summary>
  457. /// Appends a $replaceRoot stage to the pipeline.
  458. /// </summary>
  459. /// <typeparam name="TResult">The type of the result.</typeparam>
  460. /// <typeparam name="TNewResult">The type of the new result.</typeparam>
  461. /// <param name="aggregate">The aggregate.</param>
  462. /// <param name="newRoot">The new root.</param>
  463. /// <returns>
  464. /// The fluent aggregate interface.
  465. /// </returns>
  466. public static IAggregateFluent<TNewResult> ReplaceRoot<TResult, TNewResult>(
  467. this IAggregateFluent<TResult> aggregate,
  468. Expression<Func<TResult, TNewResult>> newRoot)
  469. {
  470. Ensure.IsNotNull(aggregate, nameof(aggregate));
  471. return aggregate.AppendStage(PipelineStageDefinitionBuilder.ReplaceRoot(newRoot));
  472. }
  473. /// <summary>
  474. /// Appends an ascending sort stage to the pipeline.
  475. /// </summary>
  476. /// <typeparam name="TResult">The type of the result.</typeparam>
  477. /// <param name="aggregate">The aggregate.</param>
  478. /// <param name="field">The field to sort by.</param>
  479. /// <returns>
  480. /// The fluent aggregate interface.
  481. /// </returns>
  482. public static IOrderedAggregateFluent<TResult> SortBy<TResult>(this IAggregateFluent<TResult> aggregate, Expression<Func<TResult, object>> field)
  483. {
  484. Ensure.IsNotNull(aggregate, nameof(aggregate));
  485. Ensure.IsNotNull(field, nameof(field));
  486. var sort = Builders<TResult>.Sort.Ascending(field);
  487. return (IOrderedAggregateFluent<TResult>)aggregate.AppendStage(PipelineStageDefinitionBuilder.Sort(sort));
  488. }
  489. /// <summary>
  490. /// Appends a sortByCount stage to the pipeline.
  491. /// </summary>
  492. /// <typeparam name="TResult">The type of the result.</typeparam>
  493. /// <typeparam name="TKey">The type of the key.</typeparam>
  494. /// <param name="aggregate">The aggregate.</param>
  495. /// <param name="id">The id.</param>
  496. /// <returns>
  497. /// The fluent aggregate interface.
  498. /// </returns>
  499. public static IAggregateFluent<AggregateSortByCountResult<TKey>> SortByCount<TResult, TKey>(
  500. this IAggregateFluent<TResult> aggregate,
  501. Expression<Func<TResult, TKey>> id)
  502. {
  503. Ensure.IsNotNull(aggregate, nameof(aggregate));
  504. return aggregate.AppendStage(PipelineStageDefinitionBuilder.SortByCount(id));
  505. }
  506. /// <summary>
  507. /// Appends a descending sort stage to the pipeline.
  508. /// </summary>
  509. /// <typeparam name="TResult">The type of the result.</typeparam>
  510. /// <param name="aggregate">The aggregate.</param>
  511. /// <param name="field">The field to sort by.</param>
  512. /// <returns>
  513. /// The fluent aggregate interface.
  514. /// </returns>
  515. public static IOrderedAggregateFluent<TResult> SortByDescending<TResult>(this IAggregateFluent<TResult> aggregate, Expression<Func<TResult, object>> field)
  516. {
  517. Ensure.IsNotNull(aggregate, nameof(aggregate));
  518. Ensure.IsNotNull(field, nameof(field));
  519. var sort = Builders<TResult>.Sort.Descending(field);
  520. return (IOrderedAggregateFluent<TResult>)aggregate.AppendStage(PipelineStageDefinitionBuilder.Sort(sort));
  521. }
  522. /// <summary>
  523. /// Modifies the current sort stage by appending an ascending field specification to it.
  524. /// </summary>
  525. /// <typeparam name="TResult">The type of the result.</typeparam>
  526. /// <param name="aggregate">The aggregate.</param>
  527. /// <param name="field">The field to sort by.</param>
  528. /// <returns>
  529. /// The fluent aggregate interface.
  530. /// </returns>
  531. public static IOrderedAggregateFluent<TResult> ThenBy<TResult>(this IOrderedAggregateFluent<TResult> aggregate, Expression<Func<TResult, object>> field)
  532. {
  533. Ensure.IsNotNull(aggregate, nameof(aggregate));
  534. return aggregate.ThenBy(Builders<TResult>.Sort.Ascending(field));
  535. }
  536. /// <summary>
  537. /// Modifies the current sort stage by appending a descending field specification to it.
  538. /// </summary>
  539. /// <typeparam name="TResult">The type of the result.</typeparam>
  540. /// <param name="aggregate">The aggregate.</param>
  541. /// <param name="field">The field to sort by.</param>
  542. /// <returns>
  543. /// The fluent aggregate interface.
  544. /// </returns>
  545. public static IOrderedAggregateFluent<TResult> ThenByDescending<TResult>(this IOrderedAggregateFluent<TResult> aggregate, Expression<Func<TResult, object>> field)
  546. {
  547. Ensure.IsNotNull(aggregate, nameof(aggregate));
  548. return aggregate.ThenBy(Builders<TResult>.Sort.Descending(field));
  549. }
  550. /// <summary>
  551. /// Appends an unwind stage to the pipeline.
  552. /// </summary>
  553. /// <typeparam name="TResult">The type of the result.</typeparam>
  554. /// <param name="aggregate">The aggregate.</param>
  555. /// <param name="field">The field to unwind.</param>
  556. /// <returns>
  557. /// The fluent aggregate interface.
  558. /// </returns>
  559. public static IAggregateFluent<BsonDocument> Unwind<TResult>(this IAggregateFluent<TResult> aggregate, FieldDefinition<TResult> field)
  560. {
  561. Ensure.IsNotNull(aggregate, nameof(aggregate));
  562. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Unwind(field));
  563. }
  564. /// <summary>
  565. /// Appends an unwind stage to the pipeline.
  566. /// </summary>
  567. /// <typeparam name="TResult">The type of the result.</typeparam>
  568. /// <param name="aggregate">The aggregate.</param>
  569. /// <param name="field">The field to unwind.</param>
  570. /// <returns>
  571. /// The fluent aggregate interface.
  572. /// </returns>
  573. public static IAggregateFluent<BsonDocument> Unwind<TResult>(this IAggregateFluent<TResult> aggregate, Expression<Func<TResult, object>> field)
  574. {
  575. Ensure.IsNotNull(aggregate, nameof(aggregate));
  576. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Unwind(field));
  577. }
  578. /// <summary>
  579. /// Appends an unwind stage to the pipeline.
  580. /// </summary>
  581. /// <typeparam name="TResult">The type of the result.</typeparam>
  582. /// <typeparam name="TNewResult">The type of the new result.</typeparam>
  583. /// <param name="aggregate">The aggregate.</param>
  584. /// <param name="field">The field to unwind.</param>
  585. /// <param name="newResultSerializer">The new result serializer.</param>
  586. /// <returns>
  587. /// The fluent aggregate interface.
  588. /// </returns>
  589. [Obsolete("Use the Unwind overload which takes an options parameter.")]
  590. public static IAggregateFluent<TNewResult> Unwind<TResult, TNewResult>(this IAggregateFluent<TResult> aggregate, Expression<Func<TResult, object>> field, IBsonSerializer<TNewResult> newResultSerializer)
  591. {
  592. Ensure.IsNotNull(aggregate, nameof(aggregate));
  593. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Unwind(field, new AggregateUnwindOptions<TNewResult> { ResultSerializer = newResultSerializer }));
  594. }
  595. /// <summary>
  596. /// Appends an unwind stage to the pipeline.
  597. /// </summary>
  598. /// <typeparam name="TResult">The type of the result.</typeparam>
  599. /// <typeparam name="TNewResult">The type of the new result.</typeparam>
  600. /// <param name="aggregate">The aggregate.</param>
  601. /// <param name="field">The field to unwind.</param>
  602. /// <param name="options">The options.</param>
  603. /// <returns>
  604. /// The fluent aggregate interface.
  605. /// </returns>
  606. public static IAggregateFluent<TNewResult> Unwind<TResult, TNewResult>(this IAggregateFluent<TResult> aggregate, Expression<Func<TResult, object>> field, AggregateUnwindOptions<TNewResult> options = null)
  607. {
  608. Ensure.IsNotNull(aggregate, nameof(aggregate));
  609. return aggregate.AppendStage(PipelineStageDefinitionBuilder.Unwind(field, options));
  610. }
  611. /// <summary>
  612. /// Returns the first document of the aggregate result.
  613. /// </summary>
  614. /// <typeparam name="TResult">The type of the result.</typeparam>
  615. /// <param name="aggregate">The aggregate.</param>
  616. /// <param name="cancellationToken">The cancellation token.</param>
  617. /// <returns>
  618. /// The fluent aggregate interface.
  619. /// </returns>
  620. public static TResult First<TResult>(this IAggregateFluent<TResult> aggregate, CancellationToken cancellationToken = default(CancellationToken))
  621. {
  622. Ensure.IsNotNull(aggregate, nameof(aggregate));
  623. return IAsyncCursorSourceExtensions.First(aggregate.Limit(1), cancellationToken);
  624. }
  625. /// <summary>
  626. /// Returns the first document of the aggregate result.
  627. /// </summary>
  628. /// <typeparam name="TResult">The type of the result.</typeparam>
  629. /// <param name="aggregate">The aggregate.</param>
  630. /// <param name="cancellationToken">The cancellation token.</param>
  631. /// <returns>
  632. /// The fluent aggregate interface.
  633. /// </returns>
  634. public static Task<TResult> FirstAsync<TResult>(this IAggregateFluent<TResult> aggregate, CancellationToken cancellationToken = default(CancellationToken))
  635. {
  636. Ensure.IsNotNull(aggregate, nameof(aggregate));
  637. return IAsyncCursorSourceExtensions.FirstAsync(aggregate.Limit(1), cancellationToken);
  638. }
  639. /// <summary>
  640. /// Returns the first document of the aggregate result, or the default value if the result set is empty.
  641. /// </summary>
  642. /// <typeparam name="TResult">The type of the result.</typeparam>
  643. /// <param name="aggregate">The aggregate.</param>
  644. /// <param name="cancellationToken">The cancellation token.</param>
  645. /// <returns>
  646. /// The fluent aggregate interface.
  647. /// </returns>
  648. public static TResult FirstOrDefault<TResult>(this IAggregateFluent<TResult> aggregate, CancellationToken cancellationToken = default(CancellationToken))
  649. {
  650. Ensure.IsNotNull(aggregate, nameof(aggregate));
  651. return IAsyncCursorSourceExtensions.FirstOrDefault(aggregate.Limit(1), cancellationToken);
  652. }
  653. /// <summary>
  654. /// Returns the first document of the aggregate result, or the default value if the result set is empty.
  655. /// </summary>
  656. /// <typeparam name="TResult">The type of the result.</typeparam>
  657. /// <param name="aggregate">The aggregate.</param>
  658. /// <param name="cancellationToken">The cancellation token.</param>
  659. /// <returns>
  660. /// The fluent aggregate interface.
  661. /// </returns>
  662. public static Task<TResult> FirstOrDefaultAsync<TResult>(this IAggregateFluent<TResult> aggregate, CancellationToken cancellationToken = default(CancellationToken))
  663. {
  664. Ensure.IsNotNull(aggregate, nameof(aggregate));
  665. return IAsyncCursorSourceExtensions.FirstOrDefaultAsync(aggregate.Limit(1), cancellationToken);
  666. }
  667. /// <summary>
  668. /// Returns the only document of the aggregate result. Throws an exception if the result set does not contain exactly one document.
  669. /// </summary>
  670. /// <typeparam name="TResult">The type of the result.</typeparam>
  671. /// <param name="aggregate">The aggregate.</param>
  672. /// <param name="cancellationToken">The cancellation token.</param>
  673. /// <returns>
  674. /// The fluent aggregate interface.
  675. /// </returns>
  676. public static TResult Single<TResult>(this IAggregateFluent<TResult> aggregate, CancellationToken cancellationToken = default(CancellationToken))
  677. {
  678. Ensure.IsNotNull(aggregate, nameof(aggregate));
  679. return IAsyncCursorSourceExtensions.Single(aggregate.Limit(2), cancellationToken);
  680. }
  681. /// <summary>
  682. /// Returns the only document of the aggregate result. Throws an exception if the result set does not contain exactly one document.
  683. /// </summary>
  684. /// <typeparam name="TResult">The type of the result.</typeparam>
  685. /// <param name="aggregate">The aggregate.</param>
  686. /// <param name="cancellationToken">The cancellation token.</param>
  687. /// <returns>
  688. /// The fluent aggregate interface.
  689. /// </returns>
  690. public static Task<TResult> SingleAsync<TResult>(this IAggregateFluent<TResult> aggregate, CancellationToken cancellationToken = default(CancellationToken))
  691. {
  692. Ensure.IsNotNull(aggregate, nameof(aggregate));
  693. return IAsyncCursorSourceExtensions.SingleAsync(aggregate.Limit(2), cancellationToken);
  694. }
  695. /// <summary>
  696. /// Returns the only document of the aggregate result, or the default value if the result set is empty. Throws an exception if the result set contains more than one document.
  697. /// </summary>
  698. /// <typeparam name="TResult">The type of the result.</typeparam>
  699. /// <param name="aggregate">The aggregate.</param>
  700. /// <param name="cancellationToken">The cancellation token.</param>
  701. /// <returns>
  702. /// The fluent aggregate interface.
  703. /// </returns>
  704. public static TResult SingleOrDefault<TResult>(this IAggregateFluent<TResult> aggregate, CancellationToken cancellationToken = default(CancellationToken))
  705. {
  706. Ensure.IsNotNull(aggregate, nameof(aggregate));
  707. return IAsyncCursorSourceExtensions.SingleOrDefault(aggregate.Limit(2), cancellationToken);
  708. }
  709. /// <summary>
  710. /// Returns the only document of the aggregate result, or the default value if the result set is empty. Throws an exception if the result set contains more than one document.
  711. /// </summary>
  712. /// <typeparam name="TResult">The type of the result.</typeparam>
  713. /// <param name="aggregate">The aggregate.</param>
  714. /// <param name="cancellationToken">The cancellation token.</param>
  715. /// <returns>
  716. /// The fluent aggregate interface.
  717. /// </returns>
  718. public static Task<TResult> SingleOrDefaultAsync<TResult>(this IAggregateFluent<TResult> aggregate, CancellationToken cancellationToken = default(CancellationToken))
  719. {
  720. Ensure.IsNotNull(aggregate, nameof(aggregate));
  721. return IAsyncCursorSourceExtensions.SingleOrDefaultAsync(aggregate.Limit(2), cancellationToken);
  722. }
  723. }
  724. }