PageRenderTime 39ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/MongoDB.Driver.Tests/Linq/Translators/AggregateGroupTranslatorTests.cs

http://github.com/mongodb/mongo-csharp-driver
C# | 433 lines | 305 code | 114 blank | 14 comment | 4 complexity | 7ad5fc9b20cddcbe33b508bac64d8fc0 MD5 | raw file
Possible License(s): Apache-2.0
  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 FluentAssertions;
  20. using MongoDB.Bson;
  21. using MongoDB.Bson.Serialization;
  22. using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
  23. using MongoDB.Driver.Linq;
  24. using MongoDB.Driver.Linq.Translators;
  25. using Xunit;
  26. namespace MongoDB.Driver.Tests.Linq.Translators
  27. {
  28. public class AggregateGroupTranslatorTests : IntegrationTestBase
  29. {
  30. [Fact]
  31. public void Should_translate_using_non_anonymous_type_with_default_constructor()
  32. {
  33. var result = Group(x => x.A, g => new RootView { Property = g.Key, Field = g.First().B });
  34. result.Projection.Should().Be("{ _id: \"$A\", Field: { \"$first\" : \"$B\" } }");
  35. result.Value.Property.Should().Be("Amazing");
  36. result.Value.Field.Should().Be("Baby");
  37. }
  38. [Fact]
  39. public void Should_translate_using_non_anonymous_type_with_parameterized_constructor()
  40. {
  41. var result = Group(x => x.A, g => new RootView(g.Key) { Field = g.First().B });
  42. result.Projection.Should().Be("{ _id: \"$A\", Field: { \"$first\" : \"$B\" } }");
  43. result.Value.Property.Should().Be("Amazing");
  44. result.Value.Field.Should().Be("Baby");
  45. }
  46. [Fact]
  47. public void Should_translate_just_id()
  48. {
  49. var result = Group(x => x.A, g => new { _id = g.Key });
  50. result.Projection.Should().Be("{ _id: \"$A\" }");
  51. result.Value._id.Should().Be("Amazing");
  52. }
  53. [Fact]
  54. public void Should_translate_id_when_not_named_specifically()
  55. {
  56. var result = Group(x => x.A, g => new { Test = g.Key });
  57. result.Projection.Should().Be("{ _id: \"$A\" }");
  58. result.Value.Test.Should().Be("Amazing");
  59. }
  60. [Fact]
  61. public void Should_translate_addToSet()
  62. {
  63. var result = Group(x => x.A, g => new { Result = new HashSet<int>(g.Select(x => x.C.E.F)) });
  64. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$addToSet\": \"$C.E.F\" } }");
  65. result.Value.Result.Should().Equal(111);
  66. }
  67. [Fact]
  68. public void Should_translate_addToSet_using_Distinct()
  69. {
  70. var result = Group(x => x.A, g => new { Result = g.Select(x => x.C.E.F).Distinct() });
  71. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$addToSet\": \"$C.E.F\" } }");
  72. result.Value.Result.Should().Equal(111);
  73. }
  74. [Fact]
  75. public void Should_translate_average_with_embedded_projector()
  76. {
  77. var result = Group(x => x.A, g => new { Result = g.Average(x => x.C.E.F) });
  78. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$avg\": \"$C.E.F\" } }");
  79. result.Value.Result.Should().Be(111);
  80. }
  81. [Fact]
  82. public void Should_translate_average_with_selected_projector()
  83. {
  84. var result = Group(x => x.A, g => new { Result = g.Select(x => x.C.E.F).Average() });
  85. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$avg\": \"$C.E.F\" } }");
  86. result.Value.Result.Should().Be(111);
  87. }
  88. [Fact]
  89. public void Should_translate_count()
  90. {
  91. var result = Group(x => x.A, g => new { Result = g.Count() });
  92. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$sum\": 1 } }");
  93. result.Value.Result.Should().Be(1);
  94. }
  95. [Fact]
  96. public void Should_translate_count_with_a_predicate()
  97. {
  98. var result = Group(x => x.A, g => new { Result = g.Count(x => x.A != "Awesome") });
  99. result.Projection.Should().Be("{ \"_id\" : \"$A\", \"Result\" : { \"$sum\" : { \"$cond\" : [{ \"$ne\" : [\"$A\", \"Awesome\"] }, 1, 0] } } }");
  100. result.Value.Result.Should().Be(1);
  101. }
  102. [Fact]
  103. public void Should_translate_where_with_a_predicate_and_count()
  104. {
  105. var result = Group(x => x.A, g => new { Result = g.Where(x => x.A != "Awesome").Count() });
  106. result.Projection.Should().Be("{ \"_id\" : \"$A\", \"Result\" : { \"$sum\" : { \"$cond\" : [{ \"$ne\" : [\"$A\", \"Awesome\"] }, 1, 0] } } }");
  107. result.Value.Result.Should().Be(1);
  108. }
  109. [Fact]
  110. public void Should_translate_where_select_and_count_with_predicates()
  111. {
  112. var result = Group(x => x.A, g => new { Result = g.Select(x => new { A = x.A }).Count(x => x.A != "Awesome") });
  113. result.Projection.Should().Be("{ \"_id\" : \"$A\", \"Result\" : { \"$sum\" : { \"$cond\" : [{ \"$ne\" : [\"$A\", \"Awesome\"] }, 1, 0] } } }");
  114. result.Value.Result.Should().Be(1);
  115. }
  116. [Fact]
  117. public void Should_translate_where_select_with_predicate_and_count()
  118. {
  119. var result = Group(x => x.A, g => new { Result = g.Select(x => new { A = x.A }).Count() });
  120. result.Projection.Should().Be("{ \"_id\" : \"$A\", \"Result\" : { \"$sum\" : 1 } }");
  121. result.Value.Result.Should().Be(1);
  122. }
  123. [Fact]
  124. public void Should_translate_long_count()
  125. {
  126. var result = Group(x => x.A, g => new { Result = g.LongCount() });
  127. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$sum\": 1 } }");
  128. result.Value.Result.Should().Be(1);
  129. }
  130. [Fact]
  131. public void Should_translate_first()
  132. {
  133. var result = Group(x => x.A, g => new { B = g.Select(x => x.B).First() });
  134. result.Projection.Should().Be("{ _id: \"$A\", B: { \"$first\": \"$B\" } }");
  135. result.Value.B.Should().Be("Baby");
  136. }
  137. [Fact]
  138. public void Should_translate_first_with_normalization()
  139. {
  140. var result = Group(x => x.A, g => new { g.First().B });
  141. result.Projection.Should().Be("{ _id: \"$A\", B: { \"$first\": \"$B\" } }");
  142. result.Value.B.Should().Be("Baby");
  143. }
  144. [Fact]
  145. public void Should_translate_last()
  146. {
  147. var result = Group(x => x.A, g => new { B = g.Select(x => x.B).Last() });
  148. result.Projection.Should().Be("{ _id: \"$A\", B: { \"$last\": \"$B\" } }");
  149. result.Value.B.Should().Be("Baby");
  150. }
  151. [Fact]
  152. public void Should_translate_last_with_normalization()
  153. {
  154. var result = Group(x => x.A, g => new { g.Last().B });
  155. result.Projection.Should().Be("{ _id: \"$A\", B: { \"$last\": \"$B\" } }");
  156. result.Value.B.Should().Be("Baby");
  157. }
  158. [Fact]
  159. public void Should_throw_an_exception_when_last_is_used_with_a_predicate()
  160. {
  161. Action act = () => Group(x => x.A, g => new { g.Last(x => x.A == "bin").B });
  162. act.ShouldThrow<NotSupportedException>();
  163. }
  164. [Fact]
  165. public void Should_translate_max_with_embedded_projector()
  166. {
  167. var result = Group(x => x.A, g => new { Result = g.Max(x => x.C.E.F) });
  168. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$max\": \"$C.E.F\" } }");
  169. result.Value.Result.Should().Be(111);
  170. }
  171. [Fact]
  172. public void Should_translate_max_with_selected_projector()
  173. {
  174. var result = Group(x => x.A, g => new { Result = g.Select(x => x.C.E.F).Max() });
  175. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$max\": \"$C.E.F\" } }");
  176. result.Value.Result.Should().Be(111);
  177. }
  178. [Fact]
  179. public void Should_translate_min_with_embedded_projector()
  180. {
  181. var result = Group(x => x.A, g => new { Result = g.Min(x => x.C.E.F) });
  182. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$min\": \"$C.E.F\" } }");
  183. result.Value.Result.Should().Be(111);
  184. }
  185. [Fact]
  186. public void Should_translate_min_with_selected_projector()
  187. {
  188. var result = Group(x => x.A, g => new { Result = g.Select(x => x.C.E.F).Min() });
  189. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$min\": \"$C.E.F\" } }");
  190. result.Value.Result.Should().Be(111);
  191. }
  192. [Fact]
  193. public void Should_translate_push_with_just_a_select()
  194. {
  195. var result = Group(x => x.A, g => new { Result = g.Select(x => x.C.E.F) });
  196. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$push\": \"$C.E.F\" } }");
  197. result.Value.Result.Should().Equal(111);
  198. }
  199. [Fact]
  200. public void Should_translate_push_with_ToArray()
  201. {
  202. var result = Group(x => x.A, g => new { Result = g.Select(x => x.C.E.F).ToArray() });
  203. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$push\": \"$C.E.F\" } }");
  204. result.Value.Result.Should().Equal(111);
  205. }
  206. [Fact]
  207. public void Should_translate_push_with_new_list()
  208. {
  209. var result = Group(x => x.A, g => new { Result = new List<int>(g.Select(x => x.C.E.F)) });
  210. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$push\": \"$C.E.F\" } }");
  211. result.Value.Result.Should().Equal(111);
  212. }
  213. [Fact]
  214. public void Should_translate_push_with_ToList()
  215. {
  216. var result = Group(x => x.A, g => new { Result = g.Select(x => x.C.E.F).ToList() });
  217. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$push\": \"$C.E.F\" } }");
  218. result.Value.Result.Should().Equal(111);
  219. }
  220. [SkippableFact]
  221. public void Should_translate_stdDevPop_with_embedded_projector()
  222. {
  223. RequireServer.Check().VersionGreaterThanOrEqualTo("3.1.7");
  224. var result = Group(x => 1, g => new { Result = g.StandardDeviationPopulation(x => x.C.E.F) });
  225. result.Projection.Should().Be("{ _id: 1, Result: { \"$stdDevPop\": \"$C.E.F\" } }");
  226. result.Value.Result.Should().Be(50);
  227. }
  228. [SkippableFact]
  229. public void Should_translate_stdDevPop_with_selected_projector()
  230. {
  231. RequireServer.Check().VersionGreaterThanOrEqualTo("3.1.7");
  232. var result = Group(x => 1, g => new { Result = g.Select(x => x.C.E.F).StandardDeviationPopulation() });
  233. result.Projection.Should().Be("{ _id: 1, Result: { \"$stdDevPop\": \"$C.E.F\" } }");
  234. result.Value.Result.Should().Be(50);
  235. }
  236. [SkippableFact]
  237. public void Should_translate_stdDevSamp_with_embedded_projector()
  238. {
  239. RequireServer.Check().VersionGreaterThanOrEqualTo("3.1.7");
  240. var result = Group(x => 1, g => new { Result = g.StandardDeviationSample(x => x.C.E.F) });
  241. result.Projection.Should().Be("{ _id: 1, Result: { \"$stdDevSamp\": \"$C.E.F\" } }");
  242. result.Value.Result.Should().BeApproximately(70.7106781156545, .0001);
  243. }
  244. [SkippableFact]
  245. public void Should_translate_stdDevSamp_with_selected_projector()
  246. {
  247. RequireServer.Check().VersionGreaterThanOrEqualTo("3.1.7");
  248. var result = Group(x => 1, g => new { Result = g.Select(x => x.C.E.F).StandardDeviationSample() });
  249. result.Projection.Should().Be("{ _id: 1, Result: { \"$stdDevSamp\": \"$C.E.F\" } }");
  250. result.Value.Result.Should().BeApproximately(70.7106781156545, .0001);
  251. }
  252. [Fact]
  253. public void Should_translate_sum_with_embedded_projector()
  254. {
  255. var result = Group(x => x.A, g => new { Result = g.Sum(x => x.C.E.F) });
  256. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$sum\": \"$C.E.F\" } }");
  257. result.Value.Result.Should().Be(111);
  258. }
  259. [Fact]
  260. public void Should_translate_sum_with_selected_projector()
  261. {
  262. var result = Group(x => x.A, g => new { Result = g.Select(x => x.C.E.F).Sum() });
  263. result.Projection.Should().Be("{ _id: \"$A\", Result: { \"$sum\": \"$C.E.F\" } }");
  264. result.Value.Result.Should().Be(111);
  265. }
  266. [Fact]
  267. public void Should_translate_complex_selector()
  268. {
  269. var result = Group(x => x.A, g => new
  270. {
  271. Count = g.Count(),
  272. Sum = g.Sum(x => x.C.E.F + x.C.E.H),
  273. First = g.First().B,
  274. Last = g.Last().K,
  275. Min = g.Min(x => x.C.E.F + x.C.E.H),
  276. Max = g.Max(x => x.C.E.F + x.C.E.H)
  277. });
  278. result.Projection.Should().Be("{ _id : \"$A\", Count : { \"$sum\" : 1 }, Sum : { \"$sum\" : { \"$add\": [\"$C.E.F\", \"$C.E.H\"] } }, First : { \"$first\" : \"$B\" }, Last : { \"$last\" : \"$K\" }, Min : { \"$min\" : { \"$add\" : [\"$C.E.F\", \"$C.E.H\"] } }, Max : { \"$max\" : { \"$add\" : [\"$C.E.F\", \"$C.E.H\"] } } }");
  279. result.Value.Count.Should().Be(1);
  280. result.Value.Sum.Should().Be(333);
  281. result.Value.First.Should().Be("Baby");
  282. result.Value.Last.Should().Be(false);
  283. result.Value.Min.Should().Be(333);
  284. result.Value.Max.Should().Be(333);
  285. }
  286. [Fact]
  287. public void Should_translate_aggregate_expressions_with_user_provided_serializer_if_possible()
  288. {
  289. var result = Group(x => 1, g => new
  290. {
  291. Sum = g.Sum(x => x.U)
  292. });
  293. result.Projection.Should().Be("{ _id : 1, Sum : { \"$sum\" : \"$U\" } }");
  294. result.Value.Sum.Should().Be(-0.00000000714529169165701m);
  295. }
  296. private ProjectedResult<TResult> Group<TKey, TResult>(Expression<Func<Root, TKey>> idProjector, Expression<Func<IGrouping<TKey, Root>, TResult>> groupProjector)
  297. {
  298. return Group(idProjector, groupProjector, null);
  299. }
  300. private ProjectedResult<TResult> Group<TKey, TResult>(Expression<Func<Root, TKey>> idProjector, Expression<Func<IGrouping<TKey, Root>, TResult>> groupProjector, ExpressionTranslationOptions translationOptions)
  301. {
  302. var serializer = BsonSerializer.SerializerRegistry.GetSerializer<Root>();
  303. var projectionInfo = AggregateGroupTranslator.Translate<TKey, Root, TResult>(idProjector, groupProjector, serializer, BsonSerializer.SerializerRegistry, translationOptions);
  304. var group = new BsonDocument("$group", projectionInfo.Document);
  305. var sort = new BsonDocument("$sort", new BsonDocument("_id", 1));
  306. var list = __collection.Aggregate<TResult>(new BsonDocumentStagePipelineDefinition<Root, TResult>(new[] { group, sort }, projectionInfo.ProjectionSerializer)).ToList();
  307. return new ProjectedResult<TResult>
  308. {
  309. Projection = projectionInfo.Document,
  310. Value = (TResult)list[0]
  311. };
  312. }
  313. private class ProjectedResult<T>
  314. {
  315. public BsonDocument Projection;
  316. public T Value;
  317. }
  318. }
  319. }