ProjectDocumentSource.js 7.5 KB

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