UnwindDocumentSource.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. "use strict";
  2. var assert = require("assert"),
  3. DocumentSource = require("../../../../lib/pipeline/documentSources/DocumentSource"),
  4. UnwindDocumentSource = require("../../../../lib/pipeline/documentSources/UnwindDocumentSource"),
  5. CursorDocumentSource = require("../../../../lib/pipeline/documentSources/CursorDocumentSource"),
  6. Cursor = require("../../../../lib/Cursor");
  7. //HELPERS
  8. var assertExhausted = function assertExhausted(pds) {
  9. assert.ok(pds.eof());
  10. assert.ok(!pds.advance());
  11. };
  12. /**
  13. * Tests if the given rep is the same as what the pds resolves to as JSON.
  14. * MUST CALL WITH A PDS AS THIS (e.g. checkJsonRepresentation.call(this, rep) where this is a PDS)
  15. **/
  16. var checkJsonRepresentation = function checkJsonRepresentation(self, rep) {
  17. var pdsRep = {};
  18. self.sourceToJson(pdsRep, true);
  19. assert.deepEqual(pdsRep, rep);
  20. };
  21. var createUnwind = function createUnwind(unwind) {
  22. //let unwind be optional
  23. if (!unwind) {
  24. unwind = "$a";
  25. }
  26. var spec = {"$unwind": unwind},
  27. specElement = unwind,
  28. unwindDs = UnwindDocumentSource.createFromJson(specElement);
  29. checkJsonRepresentation(unwindDs, spec);
  30. return unwindDs;
  31. };
  32. var addSource = function addSource(unwind, data) {
  33. var cwc = new CursorDocumentSource.CursorWithContext();
  34. cwc._cursor = new Cursor(data);
  35. var cds = new CursorDocumentSource(cwc);
  36. var pds = new UnwindDocumentSource();
  37. unwind.setSource(cds);
  38. };
  39. var checkResults = function checkResults(data, expectedResults, path) {
  40. //poplateData?
  41. //createSource?
  42. var unwind = createUnwind(path);
  43. addSource(unwind, data || []);
  44. expectedResults = expectedResults || [];
  45. //Load the results from the DocumentSourceUnwind
  46. var resultSet = [];
  47. while (!unwind.eof()) {
  48. // If not eof, current is non null.
  49. assert.ok(unwind.getCurrent());
  50. // Get the current result.
  51. resultSet.push(unwind.getCurrent());
  52. // Advance.
  53. if (unwind.advance()) {
  54. // If advance succeeded, eof() is false.
  55. assert.equal(unwind.eof(), false);
  56. }
  57. }
  58. // Verify the DocumentSourceUnwind is exhausted.
  59. assertExhausted(unwind);
  60. assert.deepEqual(resultSet, expectedResults);
  61. };
  62. var throwsException = function throwsException(data, path, expectedResults) {
  63. assert.throws(function () {
  64. checkResults(data, path, expectedResults);
  65. });
  66. };
  67. //TESTS
  68. module.exports = {
  69. "UnwindDocumentSource": {
  70. "constructor()": {
  71. "should not throw Error when constructing without args": function (){
  72. assert.doesNotThrow(function(){
  73. new UnwindDocumentSource();
  74. });
  75. }
  76. },
  77. "#getSourceName()": {
  78. "should return the correct source name; $unwind": function (){
  79. var pds = new UnwindDocumentSource();
  80. assert.strictEqual(pds.getSourceName(), "$unwind");
  81. }
  82. },
  83. "#eof()": {
  84. "should return true if source is empty": function (){
  85. var pds = createUnwind();
  86. addSource(pds, []);
  87. assert.strictEqual(pds.eof(), true);
  88. },
  89. "should return false if source documents exist": function (){
  90. var pds = createUnwind();
  91. addSource(pds, [{_id:0, a:[1]}]);
  92. assert.strictEqual(pds.eof(), false);
  93. }
  94. },
  95. "#advance()": {
  96. "should return false if there are no documents in the parent source": function () {
  97. var pds = createUnwind();
  98. addSource(pds, []);
  99. assert.strictEqual(pds.advance(), false);
  100. },
  101. "should return true if source documents exist and advance the source": function (){
  102. var pds = createUnwind();
  103. addSource(pds, [{_id:0, a:[1,2]}]);
  104. assert.strictEqual(pds.advance(), true);
  105. assert.strictEqual(pds.getCurrent().a, 2);
  106. }
  107. },
  108. "#getCurrent()": {
  109. "should return null if there are no documents in the parent source": function () {
  110. var pds = createUnwind();
  111. addSource(pds, []);
  112. assert.strictEqual(pds.getCurrent(), null);
  113. },
  114. "should return unwound documents": function (){
  115. var pds = createUnwind();
  116. addSource(pds, [{_id:0, a:[1,2]}]);
  117. assert.ok(pds.getCurrent());
  118. assert.strictEqual(pds.getCurrent().a, 1);
  119. },
  120. "A document without the unwind field produces no results.": function(){
  121. checkResults([{}]);
  122. },
  123. "A document with a null field produces no results.": function(){
  124. checkResults([{a:null}]);
  125. },
  126. "A document with an empty array produces no results.": function(){
  127. checkResults([{a:[]}]);
  128. },
  129. "A document with a number field produces a UserException.": function(){
  130. throwsException([{a:1}]);
  131. },
  132. "An additional document with a number field produces a UserException.": function(){
  133. throwsException([{a:[1]}, {a:1}]);
  134. },
  135. "A document with a string field produces a UserException.": function(){
  136. throwsException([{a:"foo"}]);
  137. },
  138. "A document with an object field produces a UserException.": function(){
  139. throwsException([{a:{}}]);
  140. },
  141. "Unwind an array with one value.": function(){
  142. checkResults(
  143. [{_id:0, a:[1]}],
  144. [{_id:0,a:1}]
  145. );
  146. },
  147. "Unwind an array with two values.": function(){
  148. checkResults(
  149. [{_id:0, a:[1, 2]}],
  150. [{_id:0,a:1}, {_id:0,a:2}]
  151. );
  152. },
  153. "Unwind an array with two values, one of which is null.": function(){
  154. checkResults(
  155. [{_id:0, a:[1, null]}],
  156. [{_id:0,a:1}, {_id:0,a:null}]
  157. );
  158. },
  159. "Unwind two documents with arrays.": function(){
  160. checkResults(
  161. [{_id:0, a:[1,2]}, {_id:0, a:[3,4]}],
  162. [{_id:0,a:1}, {_id:0,a:2}, {_id:0,a:3}, {_id:0,a:4}]
  163. );
  164. },
  165. "Unwind an array in a nested document.": function(){
  166. checkResults(
  167. [{_id:0,a:{b:[1,2],c:3}}],
  168. [{_id:0,a:{b:1,c:3}},{_id:0,a:{b:2,c:3}}],
  169. "$a.b"
  170. );
  171. },
  172. "A missing array (that cannot be nested below a non object field) produces no results.": function(){
  173. checkResults(
  174. [{_id:0,a:4}],
  175. [],
  176. "$a.b"
  177. );
  178. },
  179. "Unwind an array in a doubly nested document.": function(){
  180. checkResults(
  181. [{_id:0,a:{b:{d:[1,2],e:4},c:3}}],
  182. [{_id:0,a:{b:{d:1,e:4},c:3}},{_id:0,a:{b:{d:2,e:4},c:3}}],
  183. "$a.b.d"
  184. );
  185. },
  186. "Unwind several documents in a row.": function(){
  187. checkResults(
  188. [
  189. {_id:0,a:[1,2,3]},
  190. {_id:1},
  191. {_id:2},
  192. {_id:3,a:[10,20]},
  193. {_id:4,a:[30]}
  194. ],
  195. [
  196. {_id:0,a:1},
  197. {_id:0,a:2},
  198. {_id:0,a:3},
  199. {_id:3,a:10},
  200. {_id:3,a:20},
  201. {_id:4,a:30}
  202. ]
  203. );
  204. },
  205. "Unwind several more documents in a row.": function(){
  206. checkResults(
  207. [
  208. {_id:0,a:null},
  209. {_id:1},
  210. {_id:2,a:['a','b']},
  211. {_id:3},
  212. {_id:4,a:[1,2,3]},
  213. {_id:5,a:[4,5,6]},
  214. {_id:6,a:[7,8,9]},
  215. {_id:7,a:[]}
  216. ],
  217. [
  218. {_id:2,a:'a'},
  219. {_id:2,a:'b'},
  220. {_id:4,a:1},
  221. {_id:4,a:2},
  222. {_id:4,a:3},
  223. {_id:5,a:4},
  224. {_id:5,a:5},
  225. {_id:5,a:6},
  226. {_id:6,a:7},
  227. {_id:6,a:8},
  228. {_id:6,a:9}
  229. ]
  230. );
  231. }
  232. },
  233. "#createFromJson()": {
  234. "should error if called with non-string": function testNonObjectPassed() {
  235. //Date as arg
  236. assert.throws(function() {
  237. var pds = createUnwind(new Date());
  238. });
  239. //Array as arg
  240. assert.throws(function() {
  241. var pds = createUnwind([]);
  242. });
  243. //Empty args
  244. assert.throws(function() {
  245. var pds = UnwindDocumentSource.createFromJson();
  246. });
  247. //Top level operator
  248. assert.throws(function() {
  249. var pds = createUnwind({$add: []});
  250. });
  251. }
  252. },
  253. "#getDependencies": {
  254. "should get dependent field paths": function () {
  255. var pds = createUnwind("$x.y.z"),
  256. deps = {};
  257. assert.strictEqual(pds.getDependencies(deps), DocumentSource.GetDepsReturn.SEE_NEXT);
  258. assert.deepEqual(deps, {"x.y.z":1});
  259. }
  260. }
  261. }
  262. };
  263. if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);