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

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

http://github.com/mongodb/mongo-csharp-driver
C# | 351 lines | 250 code | 87 blank | 14 comment | 0 complexity | 9160cf53839a1bb7eeb026c6e50d1b00 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.IO;
  22. using MongoDB.Bson.Serialization;
  23. using MongoDB.Driver.Linq.Translators;
  24. using Xunit;
  25. namespace MongoDB.Driver.Tests.Linq.Translators
  26. {
  27. public class FindProjectionTranslatorTests
  28. {
  29. [Fact]
  30. public void Should_not_translate_identity()
  31. {
  32. var result = Project(p => p, "{ _id: 0, A: \"Jack\", B: \"Awesome\" }");
  33. result.Projection.Should().BeNull();
  34. result.Value.A.Should().Be("Jack");
  35. result.Value.B.Should().Be("Awesome");
  36. }
  37. [Fact]
  38. public void Should_not_translate_when_identity_is_present()
  39. {
  40. var result = Project(p => new { P = p, Combined = p.A + " " + p.B }, "{ _id: 0, A: \"Jack\", B: \"Awesome\" }");
  41. result.Projection.Should().BeNull();
  42. result.Value.P.A.Should().Be("Jack");
  43. result.Value.P.B.Should().Be("Awesome");
  44. result.Value.Combined.Should().Be("Jack Awesome");
  45. }
  46. [Fact]
  47. public void Should_include_id_if_specified()
  48. {
  49. var result = Project(p => new { p.Id, p.A }, "{ _id: 1, A: \"Jack\" }");
  50. result.Projection.Should().Be("{ _id: 1, A: 1 }");
  51. result.Value.Id.Should().Be(1);
  52. result.Value.A.Should().Be("Jack");
  53. }
  54. [Fact]
  55. public void Should_translate_a_single_top_level_field()
  56. {
  57. var result = Project(p => p.A, "{ A: \"Jack\" }");
  58. result.Projection.Should().Be("{ A: 1, _id: 0 }");
  59. result.Value.Should().Be("Jack");
  60. }
  61. [Fact]
  62. public void Should_translate_a_single_top_level_computed_field()
  63. {
  64. var result = Project(p => p.A + " " + p.B, "{ A: \"Jack\", B: \"Awesome\" }");
  65. result.Projection.Should().Be("{ A: 1, B: 1, _id: 0 }");
  66. result.Value.Should().Be("Jack Awesome");
  67. }
  68. [Fact]
  69. public void Should_translate_a_single_top_level_field_with_an_operation()
  70. {
  71. var result = Project(p => p.A.ToLowerInvariant(), "{ A: \"Jack\" }");
  72. result.Projection.Should().Be("{ A: 1, _id: 0 }");
  73. result.Value.Should().Be("jack");
  74. }
  75. [Fact]
  76. public void Should_translate_a_new_expression_where_one_field_starts_with_another_field()
  77. {
  78. var result = Project(p => new { p.C.E, p.C.E1 }, "{ C : { E : { F : 2, H : 3 }, E1 : { F : 4, H : 5 } } }");
  79. result.Projection.Should().Be("{ \"C.E\" : 1, \"C.E1\" : 1, _id : 0 }");
  80. result.Value.E.F.Should().Be(2);
  81. result.Value.E.H.Should().Be(3);
  82. result.Value.E1.F.Should().Be(4);
  83. result.Value.E1.H.Should().Be(5);
  84. }
  85. [Fact]
  86. public void Should_translate_a_new_expression_with_a_single_top_level_field()
  87. {
  88. var result = Project(p => new { p.A }, "{ A: \"Jack\" }");
  89. result.Projection.Should().Be("{ A: 1, _id: 0 }");
  90. result.Value.A.Should().Be("Jack");
  91. }
  92. [Fact]
  93. public void Should_translate_a_new_expression_with_a_single_top_level_computed_field()
  94. {
  95. var result = Project(p => new { FullName = p.A + " " + p.B }, "{ A: \"Jack\", B: \"Awesome\" }");
  96. result.Projection.Should().Be("{ A: 1, B: 1, _id: 0 }");
  97. result.Value.FullName.Should().Be("Jack Awesome");
  98. }
  99. [Fact]
  100. public void Should_translate_a_new_expression_with_top_level_fields_when_one_field_starts_with_another()
  101. {
  102. var result = Project(p => new { p.A, p.A1 }, "{ A : \"Jack\", A1 : \"Peter\" }");
  103. result.Projection.Should().Be("{ A : 1, A1 : 1 _id: 0 }");
  104. result.Value.A.Should().Be("Jack");
  105. result.Value.A1.Should().Be("Peter");
  106. }
  107. [Fact]
  108. public void Should_translate_when_a_top_level_field_is_repeated()
  109. {
  110. var result = Project(p => new { FirstName = p.A, FullName = p.A + " " + p.B }, "{ A: \"Jack\", B: \"Awesome\" }");
  111. result.Projection.Should().Be("{ A: 1, B: 1, _id: 0 }");
  112. result.Value.FirstName.Should().Be("Jack");
  113. result.Value.FullName.Should().Be("Jack Awesome");
  114. }
  115. [Fact]
  116. public void Should_translate_with_a_single_nested_field()
  117. {
  118. var result = Project(p => p.C.E.F, "{ C: { E: { F: 2 } } }");
  119. result.Projection.Should().Be("{ \"C.E.F\": 1, _id: 0 }");
  120. result.Value.Should().Be(2);
  121. }
  122. [Fact]
  123. public void Should_translate_with_a_single_computed_nested_field()
  124. {
  125. var result = Project(p => p.C.E.F + 10, "{ C: { E: { F: 2 } } }");
  126. result.Projection.Should().Be("{ \"C.E.F\": 1, _id: 0 }");
  127. result.Value.Should().Be(12);
  128. }
  129. [Fact]
  130. public void Should_translate_with_a_hierarchical_redundancy_and_a_non_top_level_projection()
  131. {
  132. var result = Project(p => new { p.C.E, F = p.C.E.F }, "{ C : { E : { F : 2, H : 3 } } }");
  133. result.Projection.Should().Be("{ \"C.E\" : 1, _id : 0 }");
  134. result.Value.E.H.Should().Be(3);
  135. result.Value.F.Should().Be(2);
  136. result.Value.E.F.Should().Be(2);
  137. }
  138. [Fact]
  139. public void Should_translate_with_a_hierarchical_redundancy_and_a_top_level_projection()
  140. {
  141. var result = Project(p => new { p.C, F = p.C.E.F }, "{ C: { D: \"CEO\", E: { F: 2 } } }");
  142. result.Projection.Should().Be("{ \"C\": 1, _id: 0 }");
  143. result.Value.C.D.Should().Be("CEO");
  144. result.Value.C.E.F.Should().Be(2);
  145. result.Value.F.Should().Be(2);
  146. }
  147. [Fact]
  148. public void Should_translate_with_a_hierarchical_redundancy_and_when_one_field_doesnt_start_with_another()
  149. {
  150. var result = Project(p => new { p.C.E, F = p.C.E1.F }, "{ C : { E : { H : 3 }, E1 : { F : 2 } } }");
  151. result.Projection.Should().Be("{ \"C.E\" : 1, \"C.E1.F\" : 1, _id : 0 }");
  152. result.Value.E.H.Should().Be(3);
  153. result.Value.F.Should().Be(2);
  154. }
  155. [Fact]
  156. public void Should_translate_with_a_hierarchical_redundancy_and_when_one_field_starts_with_another()
  157. {
  158. var result = Project(p => new { p.C.E, F = p.C.E.E1.F }, "{ C : { E : { E1 : { F : 2, H : 3 } } } }");
  159. result.Projection.Should().Be("{ \"C.E\" : 1, _id : 0 }");
  160. result.Value.E.E1.H.Should().Be(3);
  161. result.Value.E.E1.F.Should().Be(2);
  162. result.Value.F.Should().Be(2);
  163. }
  164. [Fact]
  165. public void Should_translate_a_single_top_level_array()
  166. {
  167. var result = Project(p => p.G, "{ G: [{ D: \"Uno\", E : { F: 1 } }, { D: \"Dos\", E: { F: 2 } }] }");
  168. result.Projection.Should().Be("{ \"G\": 1, _id: 0 }");
  169. result.Value.Count().Should().Be(2);
  170. result.Value.ElementAt(0).D.Should().Be("Uno");
  171. result.Value.ElementAt(0).E.F.Should().Be(1);
  172. result.Value.ElementAt(1).D.Should().Be("Dos");
  173. result.Value.ElementAt(1).E.F.Should().Be(2);
  174. }
  175. [Fact]
  176. public void Should_translate_through_a_single_top_level_array_using_Select()
  177. {
  178. var result = Project(p => p.G.Select(x => x.D), "{ G: [{ D: \"Uno\" }, { D: \"Dos\" }] }");
  179. result.Projection.Should().Be("{ \"G.D\": 1, _id: 0 }");
  180. result.Value.Count().Should().Be(2);
  181. result.Value.ElementAt(0).Should().Be("Uno");
  182. result.Value.ElementAt(1).Should().Be("Dos");
  183. }
  184. [Fact]
  185. public void Should_translate_through_a_single_top_level_array_using_SelectMany()
  186. {
  187. var result = Project(p => p.G.SelectMany(x => x.E.I), "{ G: [{ E: { I: [\"a\", \"b\"] } }, { E: { I: [\"c\", \"d\"] } }] }");
  188. result.Projection.Should().Be("{ \"G.E.I\": 1, _id: 0 }");
  189. result.Value.Count().Should().Be(4);
  190. result.Value.Should().BeEquivalentTo("a", "b", "c", "d");
  191. }
  192. [Fact]
  193. public void Should_translate_through_a_single_top_level_array_to_a_binary_operation()
  194. {
  195. var result = Project(p => p.G.Select(x => x.E.F + x.E.H), "{ G: [{ E: { F: 2, H: 3 } }, { E: { F: 6, H: 7 } }] }");
  196. result.Projection.Should().Be("{ \"G.E.F\": 1, \"G.E.H\": 1, _id: 0 }");
  197. result.Value.Count().Should().Be(2);
  198. result.Value.ElementAt(0).Should().Be(5);
  199. result.Value.ElementAt(1).Should().Be(13);
  200. }
  201. [Fact]
  202. public void Should_translate_through_a_single_top_level_array_when_array_identity_is_present()
  203. {
  204. var result = Project(p => new { p.G, Sums = p.G.Select(x => x.E.F + x.E.H) }, "{ G: [{ D: \"Uno\", E: { F: 2, H: 3 } }, { D: \"Dos\", E: { F: 6, H: 7 } }] }");
  205. result.Projection.Should().Be("{ G: 1, _id: 0 }");
  206. result.Value.G.ElementAt(0).D.Should().Be("Uno");
  207. result.Value.G.ElementAt(1).D.Should().Be("Dos");
  208. result.Value.Sums.Count().Should().Be(2);
  209. result.Value.Sums.ElementAt(0).Should().Be(5);
  210. result.Value.Sums.ElementAt(1).Should().Be(13);
  211. }
  212. [Fact]
  213. public void Should_translate_with_a_parent_field_in_a_child_selector()
  214. {
  215. var result = Project(p => p.G.Select(x => new { A = p.A, D = x.D }), "{ A: \"Yay\", G: [{ D: \"Uno\" }, { D: \"Dos\" }] }");
  216. result.Projection.Should().Be("{ \"A\": 1, \"G.D\": 1, _id: 0 }");
  217. result.Value.ElementAt(0).A.Should().Be("Yay");
  218. result.Value.ElementAt(0).D.Should().Be("Uno");
  219. result.Value.ElementAt(1).A.Should().Be("Yay");
  220. result.Value.ElementAt(1).D.Should().Be("Dos");
  221. }
  222. private ProjectedResult<T> Project<T>(Expression<Func<Root, T>> projector, string json)
  223. {
  224. var serializer = BsonSerializer.SerializerRegistry.GetSerializer<Root>();
  225. var projectionInfo = FindProjectionTranslator.Translate<Root, T>(projector, serializer, BsonSerializer.SerializerRegistry);
  226. using (var reader = new JsonReader(json))
  227. {
  228. var context = BsonDeserializationContext.CreateRoot(reader);
  229. return new ProjectedResult<T>
  230. {
  231. Projection = projectionInfo.Document,
  232. Value = projectionInfo.ProjectionSerializer.Deserialize(context)
  233. };
  234. }
  235. }
  236. private class ProjectedResult<T>
  237. {
  238. public BsonDocument Projection;
  239. public T Value;
  240. }
  241. private class Root
  242. {
  243. public int Id { get; set; }
  244. public string A { get; set; }
  245. public string A1 { get; set; }
  246. public string B { get; set; }
  247. public C C { get; set; }
  248. public IEnumerable<C> G { get; set; }
  249. }
  250. public class C
  251. {
  252. public string D { get; set; }
  253. public E E { get; set; }
  254. public E E1 { get; set; }
  255. }
  256. public class E
  257. {
  258. public int F { get; set; }
  259. public int H { get; set; }
  260. public IEnumerable<string> I { get; set; }
  261. public E E1 { get; set; }
  262. }
  263. }
  264. }