UnwindDocumentSource_test.js 7.6 KB

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