UnwindDocumentSource.js 8.7 KB

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