ProjectDocumentSource.js 6.7 KB

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