Pipeline.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. "use strict";
  2. var assert = require("assert"),
  3. Pipeline = require("../../../lib/pipeline/Pipeline"),
  4. FieldPath = require("../../../lib/pipeline/FieldPath"),
  5. DocumentSource = require('../../../lib/pipeline/documentSources/DocumentSource');
  6. module.exports = {
  7. "Pipeline": {
  8. before: function () {
  9. Pipeline.stageDesc.$test = (function () {
  10. var klass = function TestDocumentSource(options, ctx) {
  11. base.call(this, ctx);
  12. this.shouldCoalesce = options.coalesce;
  13. this.coalesceWasCalled = false;
  14. this.optimizeWasCalled = false;
  15. this.works = options.works === false ? false : true; // don't judge
  16. this.current = 5;
  17. }, TestDocumentSource = klass, base = DocumentSource, proto = klass.prototype = Object.create(base.prototype, {constructor: {value: klass}});
  18. proto.coalesce = function () {
  19. this.coalesceWasCalled = true;
  20. var c = this.shouldCoalesce;//only coalesce with the first thing we find
  21. this.shouldCoalesce = false;
  22. return c;
  23. };
  24. proto.optimize = function () {
  25. this.optimizeWasCalled = true;
  26. };
  27. proto.getNext = function(callback){
  28. var answer = this.current > 0 ? {val:this.current--} : DocumentSource.EOF,
  29. err = null;
  30. if (!this.works)
  31. err = new Error("doesn't work"), answer = undefined;
  32. if(callback) {
  33. return callback(err, answer);
  34. } else {
  35. return answer || err;
  36. }
  37. };
  38. klass.createFromJson = function (options, ctx) {
  39. return new TestDocumentSource(options, ctx);
  40. };
  41. return klass;
  42. })().createFromJson;
  43. },
  44. "parseCommand": {
  45. "should throw Error if given non-objects in the array": function () {
  46. assert.throws(function () {
  47. Pipeline.parseCommand({pipeline: [5]});
  48. });
  49. },
  50. "should throw Error if given objects with more / less than one field": function () {
  51. assert.throws(function () {
  52. Pipeline.parseCommand({pipeline: [
  53. {}
  54. ]});
  55. Pipeline.parseCommand({pipeline: [
  56. {a: 1, b: 2}
  57. ]});
  58. });
  59. },
  60. "should throw Error on unknown document sources": function () {
  61. assert.throws(function () {
  62. Pipeline.parseCommand({pipeline: [
  63. {$foo: "$sdfdf"}
  64. ]});
  65. });
  66. },
  67. "should swap $match and $sort if the $match immediately follows the $sort": function () {
  68. var p = Pipeline.parseCommand({pipeline: [
  69. {$sort: {"xyz": 1}},
  70. {$match: {}}
  71. ]});
  72. assert.equal(p.sourceVector[0].constructor.matchName, "$match");
  73. assert.equal(p.sourceVector[1].constructor.sortName, "$sort");
  74. },
  75. "should attempt to coalesce all sources": function () {
  76. var p = Pipeline.parseCommand({pipeline: [
  77. {$test: {coalesce: false}},
  78. {$test: {coalesce: true}},
  79. {$test: {coalesce: false}},
  80. {$test: {coalesce: false}}
  81. ]});
  82. assert.equal(p.sourceVector.length, 3);
  83. p.sourceVector.slice(0, -1).forEach(function (source) {
  84. assert.equal(source.coalesceWasCalled, true);
  85. });
  86. assert.equal(p.sources[p.sources.length -1].coalesceWasCalled, false);
  87. },
  88. "should optimize all sources": function () {
  89. var p = Pipeline.parseCommand({pipeline: [
  90. {$test: {coalesce: false}},
  91. {$test: {coalesce: false}}
  92. ]});
  93. p.sourceVector.forEach(function (source) {
  94. assert.equal(source.optimizeWasCalled, true);
  95. });
  96. }
  97. },
  98. "#stitch": {
  99. "should set the parent source for all sources in the pipeline except the first one": function () {
  100. var p = Pipeline.parseCommand({pipeline:[{$test:{coalesce:false}}, {$test:{coalesce:false}}, {$test:{coalesce:false}}]});
  101. p.stitch();
  102. assert.equal(p.sources[1].source, p.sources[0]);
  103. }
  104. },
  105. "#_runSync": {
  106. "should iterate through sources and return resultant array": function () {
  107. var p = Pipeline.parseCommand({pipeline:[{$test:{coalesce:false}}, {$test:{coalesce:false}}, {$test:{coalesce:false}}]}),
  108. results = p.run(function(err, results) {
  109. assert.deepEqual(results.result, [ { val: 5 }, { val: 4 }, { val: 3 }, { val: 2 }, { val: 1 } ]);
  110. });
  111. },
  112. "should catch parse errors": function () {
  113. // The $foo part is invalid and causes a throw.
  114. assert.throws(function () {
  115. Pipeline.parseCommand({pipeline: [
  116. {$match: {$foo: {bar: "baz"}}}
  117. ]});
  118. });
  119. },
  120. "should call callback with errors from pipeline components": function (next) {
  121. var p = Pipeline.parseCommand({pipeline: [
  122. {$match: {foo: {bar: "baz"}}}
  123. ]});
  124. p.run(new DocumentSource({}), function (err, results) {
  125. assert(err instanceof Error);
  126. return next();
  127. });
  128. }
  129. },
  130. "#_runAsync": {
  131. "should iterate through sources and return resultant array asynchronously": function () {
  132. var p = Pipeline.parseCommand({pipeline:[{$test:{coalesce:false}}, {$test:{coalesce:false}}, {$test:{coalesce:false}}]}),
  133. results = p.run(function(err, results) {
  134. assert.deepEqual(results.result, [ { val: 5 }, { val: 4 }, { val: 3 }, { val: 2 }, { val: 1 } ]);
  135. });
  136. }
  137. },
  138. "#addInitialSource": {
  139. "should put the given source at the beginning of the pipeline": function () {
  140. var p = Pipeline.parseCommand({pipeline:[{$test:{coalesce:false}}, {$test:{coalesce:false}}, {$test:{coalesce:false}}]}),
  141. initialSource = Pipeline.stageDesc.$test({coalesce:false});
  142. p.addInitialSource(initialSource);
  143. assert.equal(initialSource, p.sources[0]);
  144. },
  145. "should be able to addInitialSource then stitch": function () {
  146. var p = Pipeline.parseCommand({pipeline:[{$test:{coalesce:false}}, {$test:{coalesce:false}}, {$test:{coalesce:false}}]}),
  147. initialSource = Pipeline.stageDesc.$test({coalesce:false});
  148. p.addInitialSource(initialSource);
  149. p.stitch();
  150. assert.equal(p.sources[1].source, p.sources[0]);
  151. }
  152. }
  153. }
  154. };
  155. if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).grep(process.env.MOCHA_GREP || '').run(process.exit);