ProjectDocumentSource.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. "use strict";
  2. var assert = require("assert"),
  3. async = require("async"),
  4. DepsTracker = require("../../../../lib/pipeline/DepsTracker"),
  5. DocumentSource = require("../../../../lib/pipeline/documentSources/DocumentSource"),
  6. ProjectDocumentSource = require("../../../../lib/pipeline/documentSources/ProjectDocumentSource"),
  7. CursorDocumentSource = require("../../../../lib/pipeline/documentSources/CursorDocumentSource"),
  8. ArrayRunner = require("../../../../lib/query/ArrayRunner"),
  9. TestBase = require("./TestBase"),
  10. And = require("../../../../lib/pipeline/expressions/AndExpression"),
  11. Add = require("../../../../lib/pipeline/expressions/AddExpression");
  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 = self.serialize();
  18. assert.deepEqual(pdsRep, rep);
  19. };
  20. var createProject = function createProject(projection) {
  21. //let projection be optional
  22. if (!projection) {
  23. projection = {
  24. a: true
  25. };
  26. }
  27. var spec = {
  28. "$project": projection
  29. },
  30. specElement = projection,
  31. _project = ProjectDocumentSource.createFromJson(specElement);
  32. checkJsonRepresentation(_project, spec);
  33. return _project;
  34. };
  35. //TESTS
  36. module.exports = {
  37. "constructor()": {
  38. "should not throw Error when constructing without args": function testConstructor() {
  39. assert.doesNotThrow(function() {
  40. new ProjectDocumentSource();
  41. });
  42. },
  43. "should throw Error when constructing with more than 1 arg": function testConstructor() {
  44. assert.throws(function() {
  45. new ProjectDocumentSource("a", "b", "c");
  46. });
  47. }
  48. },
  49. "#getSourceName()": {
  50. "should return the correct source name; $project": function testSourceName() {
  51. var pds = new ProjectDocumentSource();
  52. assert.strictEqual(pds.getSourceName(), "$project");
  53. }
  54. },
  55. "#getNext()": {
  56. "should return errors in the callback": function Errors() {
  57. var input = [{_id: 0, a: "foo"}];
  58. var cds = new CursorDocumentSource(null, new ArrayRunner(input), null);
  59. var pds = ProjectDocumentSource.createFromJson({x:{"$add":["$a", "$a"]}});
  60. pds.setSource(cds);
  61. pds.getNext(function(err, actual) {
  62. assert(err, "Expected error");
  63. });
  64. },
  65. "should return EOF": function testEOF(next) {
  66. var pds = createProject({});
  67. pds.setSource({
  68. getNext: function getNext(cb) {
  69. return cb(null, null);
  70. }
  71. });
  72. pds.getNext(function(err, doc) {
  73. assert.equal(null, doc);
  74. next();
  75. });
  76. },
  77. "iterator state accessors consistently report the source is exhausted": function assertExhausted() {
  78. var input = [{}];
  79. var cds = new CursorDocumentSource(null, new ArrayRunner(input), null);
  80. var pds = createProject();
  81. pds.setSource(cds);
  82. pds.getNext(function(err, actual) {
  83. pds.getNext(function(err, actual1) {
  84. assert.equal(null, actual1);
  85. pds.getNext(function(err, actual2) {
  86. assert.equal(null, actual2);
  87. pds.getNext(function(err, actual3) {
  88. assert.equal(null, actual3);
  89. });
  90. });
  91. });
  92. });
  93. },
  94. "callback is required": function requireCallback() {
  95. var pds = createProject();
  96. assert.throws(pds.getNext.bind(pds));
  97. },
  98. "should not return EOF when a document is still in cursor": function testNotEOFTrueIfDocPresent() {
  99. var input = [{_id: 0, a: 1}, {_id: 1, a: 2}];
  100. var cds = new CursorDocumentSource(null, new ArrayRunner(input), null);
  101. var pds = createProject();
  102. pds.setSource(cds);
  103. pds.getNext(function(err,actual) {
  104. // first go round
  105. assert.notEqual(actual, null);
  106. });
  107. },
  108. "can retrieve second document from source": function testAdvanceFirst() {
  109. var input = [{_id: 0, a: 1}, {_id: 1, a: 2}];
  110. var cds = new CursorDocumentSource(null, new ArrayRunner(input), null);
  111. var pds = createProject();
  112. pds.setSource(cds);
  113. pds.getNext(function(err,val) {
  114. // eh, ignored
  115. pds.getNext(function(err,val) {
  116. assert.equal(2, val.a);
  117. });
  118. });
  119. },
  120. "should get the first document out of a cursor": function getCurrentCalledFirst() {
  121. var input = [{_id: 0, a: 1}];
  122. var cds = new CursorDocumentSource(null, new ArrayRunner(input), null);
  123. var pds = createProject();
  124. pds.setSource(cds);
  125. pds.getNext(function(err, actual) {
  126. assert.equal(1, actual.a);
  127. });
  128. },
  129. "The a and c.d fields are included but the b field is not": function testFullProject1(next) {
  130. var input = [{
  131. _id:0,
  132. a: 1,
  133. b: 1,
  134. c: {
  135. d: 1
  136. }
  137. }];
  138. var cds = new CursorDocumentSource(null, new ArrayRunner(input), null);
  139. var pds = createProject({
  140. a: true,
  141. c: {
  142. d: true
  143. }
  144. }),
  145. expected = {_id: 0, a:1, c:{ d: 1 }};
  146. pds.setSource(cds);
  147. pds.getNext(function(err,val) {
  148. assert.deepEqual(expected, val);
  149. next();
  150. });
  151. },
  152. "Two documents": function testTwoDocumentsProject(next) {
  153. var input = [{
  154. a: 1,
  155. b: 2
  156. }, {
  157. a: 3,
  158. b: 4
  159. }],
  160. expected = [
  161. {a:1},
  162. {a:3},
  163. null
  164. ];
  165. var cds = new CursorDocumentSource(null, new ArrayRunner(input), null);
  166. var pds = createProject({
  167. a: true,
  168. c: {
  169. d: true
  170. }
  171. });
  172. pds.setSource(cds);
  173. async.series([
  174. pds.getNext.bind(pds),
  175. pds.getNext.bind(pds),
  176. pds.getNext.bind(pds),
  177. ],
  178. function(err,res) {
  179. assert.deepEqual(expected, res);
  180. next();
  181. }
  182. );
  183. }
  184. },
  185. "#optimize()": {
  186. "Optimize the projection": function optimizeProject() {
  187. var pds = createProject({
  188. a: {
  189. $and: [{$const:true}]
  190. }
  191. });
  192. pds.optimize();
  193. checkJsonRepresentation(pds, {$project:{a:{$const:true}}});
  194. }
  195. },
  196. "#createFromJson()": {
  197. "should error if called with non-object": function testNonObjectPassed() {
  198. //String as arg
  199. assert.throws(function() {
  200. var pds = createProject("not an object");
  201. });
  202. //Date as arg
  203. assert.throws(function() {
  204. var pds = createProject(new Date());
  205. });
  206. //Array as arg
  207. assert.throws(function() {
  208. var pds = createProject([]);
  209. });
  210. //Empty args
  211. assert.throws(function() {
  212. var pds = ProjectDocumentSource.createFromJson();
  213. });
  214. //Top level operator
  215. assert.throws(function() {
  216. var pds = createProject({
  217. $add: []
  218. });
  219. });
  220. //Invalid spec
  221. assert.throws(function() {
  222. var pds = createProject({
  223. a: {
  224. $invalidOperator: 1
  225. }
  226. });
  227. });
  228. }
  229. },
  230. "#getDependencies()": {
  231. "should properly detect dependencies in project": function testGetDependencies() {
  232. var input = {
  233. a: true,
  234. x: '$b',
  235. y: {
  236. $and: ['$c', '$d']
  237. }
  238. };
  239. var pds = createProject(input);
  240. var dependencies = new DepsTracker();
  241. assert.equal(DocumentSource.GetDepsReturn.EXHAUSTIVE_FIELDS, pds.getDependencies(dependencies));
  242. assert.equal(5, Object.keys(dependencies.fields).length);
  243. assert.ok(dependencies.fields._id);
  244. assert.ok(dependencies.fields.a);
  245. assert.ok(dependencies.fields.b);
  246. assert.ok(dependencies.fields.c);
  247. assert.ok(dependencies.fields.d);
  248. }
  249. }
  250. };
  251. if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).grep(process.env.MOCHA_GREP || '').run(process.exit);