SortDocumentSource.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. "use strict";
  2. var assert = require("assert"),
  3. async = require("async"),
  4. DocumentSource = require("../../../../lib/pipeline/documentSources/DocumentSource"),
  5. SortDocumentSource = require("../../../../lib/pipeline/documentSources/SortDocumentSource"),
  6. LimitDocumentSource = require("../../../../lib/pipeline/documentSources/LimitDocumentSource"),
  7. CursorDocumentSource = require("../../../../lib/pipeline/documentSources/CursorDocumentSource"),
  8. Cursor = require("../../../../lib/Cursor"),
  9. FieldPathExpression = require("../../../../lib/pipeline/expressions/FieldPathExpression");
  10. module.exports = {
  11. "SortDocumentSource": {
  12. "constructor()": {
  13. "should not throw Error when constructing without args": function testConstructor(){
  14. assert.doesNotThrow(function(){
  15. new SortDocumentSource();
  16. });
  17. }
  18. },
  19. "#getSourceName()": {
  20. "should return the correct source name; $sort": function testSourceName(){
  21. var sds = new SortDocumentSource();
  22. assert.strictEqual(sds.getSourceName(), "$sort");
  23. }
  24. },
  25. "#getFactory()": {
  26. "should return the constructor for this class": function factoryIsConstructor(){
  27. assert.strictEqual(new SortDocumentSource().getFactory(), SortDocumentSource);
  28. }
  29. },
  30. "#getNext()": {
  31. "should return EOF if there are no more sources": function noSources(next){
  32. var cwc = new CursorDocumentSource.CursorWithContext();
  33. cwc._cursor = new Cursor( [{a: 1}] );
  34. var cds = new CursorDocumentSource(cwc);
  35. var sds = SortDocumentSource.createFromJson({a:1});
  36. sds.setSource(cds);
  37. sds.getNext(function(err, val) {
  38. assert.deepEqual(val, {a:1});
  39. sds.getNext(function(err, val) {
  40. assert.equal(val, DocumentSource.EOF);
  41. next();
  42. });
  43. });
  44. },
  45. "should return EOF if there are more documents": function hitSort(next){
  46. var cwc = new CursorDocumentSource.CursorWithContext();
  47. cwc._cursor = new Cursor( [{a: 1}] );
  48. var cds = new CursorDocumentSource(cwc);
  49. var sds = SortDocumentSource.createFromJson({a:1});
  50. sds.setSource(cds);
  51. sds.getNext(function(err, doc) {
  52. assert.notEqual(doc, DocumentSource.EOF);
  53. next();
  54. });
  55. },
  56. "should return the current document source": function currSource(next){
  57. var cwc = new CursorDocumentSource.CursorWithContext();
  58. cwc._cursor = new Cursor( [{a: 1}] );
  59. var cds = new CursorDocumentSource(cwc);
  60. var sds = SortDocumentSource.createFromJson({a:1});
  61. sds.setSource(cds);
  62. sds.getNext(function(err, doc) {
  63. assert.deepEqual(doc, { a:1 });
  64. next();
  65. });
  66. },
  67. "should return next document when moving to the next source": function nextSource(next){
  68. var cwc = new CursorDocumentSource.CursorWithContext();
  69. cwc._cursor = new Cursor( [{a: 1}, {b:2}] );
  70. var cds = new CursorDocumentSource(cwc);
  71. var sds = SortDocumentSource.createFromJson({a:1});
  72. sds.setSource(cds);
  73. sds.getNext(function(err, doc) {
  74. assert.deepEqual(doc, {b:2});
  75. next();
  76. });
  77. },
  78. "should return false for no sources remaining": function noMoar(next){
  79. var cwc = new CursorDocumentSource.CursorWithContext();
  80. cwc._cursor = new Cursor( [{a: 1}, {b:2}] );
  81. var cds = new CursorDocumentSource(cwc);
  82. var sds = SortDocumentSource.createFromJson({a:1});
  83. sds.setSource(cds);
  84. sds.getNext(function(err, doc) {
  85. sds.getNext(function(err, doc) {
  86. assert.deepEqual(doc, {a:1});
  87. next();
  88. });
  89. });
  90. }
  91. },
  92. "#serialize()": {
  93. "should throw an error when trying to serialize": function serialize() {
  94. var sds = new SortDocumentSource();
  95. assert.throws(sds.serialize.bind(sds));
  96. }
  97. },
  98. "#serializeToArray()": {
  99. "should create an object representation of the SortDocumentSource": function serializeToArrayTest(){
  100. var sds = new SortDocumentSource();
  101. sds.vSortKey.push(new FieldPathExpression("b") );
  102. var t = [];
  103. sds.serializeToArray(t, false);
  104. assert.deepEqual(t, [{ "$sort": { "b": -1 } }]);
  105. }
  106. },
  107. "#createFromJson()": {
  108. "should return a new SortDocumentSource object from an input JSON object": function createTest(){
  109. var sds = SortDocumentSource.createFromJson({a:1});
  110. assert.strictEqual(sds.constructor, SortDocumentSource);
  111. var t = [];
  112. sds.serializeToArray(t, false);
  113. assert.deepEqual(t, [{ "$sort": { "a": 1 } }]);
  114. },
  115. "should return a new SortDocumentSource object from an input JSON object with a descending field": function createTest(){
  116. var sds = SortDocumentSource.createFromJson({a:-1});
  117. assert.strictEqual(sds.constructor, SortDocumentSource);
  118. var t = [];
  119. sds.serializeToArray(t, false);
  120. assert.deepEqual(t, [{ "$sort": { "a": -1 } }]);
  121. },
  122. "should return a new SortDocumentSource object from an input JSON object with dotted paths": function createTest(){
  123. var sds = SortDocumentSource.createFromJson({ "a.b":1 });
  124. assert.strictEqual(sds.constructor, SortDocumentSource);
  125. var t = [];
  126. sds.serializeToArray(t, false);
  127. assert.deepEqual(t, [{ "$sort": { "a.b" : 1 } }]);
  128. },
  129. "should throw an exception when not passed an object": function createTest(){
  130. assert.throws(function() {
  131. var sds = SortDocumentSource.createFromJson(7);
  132. });
  133. },
  134. "should throw an exception when passed an empty object": function createTest(){
  135. assert.throws(function() {
  136. var sds = SortDocumentSource.createFromJson({});
  137. });
  138. },
  139. "should throw an exception when passed an object with a non number value": function createTest(){
  140. assert.throws(function() {
  141. var sds = SortDocumentSource.createFromJson({a:"b"});
  142. });
  143. },
  144. "should throw an exception when passed an object with a non valid number value": function createTest(){
  145. assert.throws(function() {
  146. var sds = SortDocumentSource.createFromJson({a:14});
  147. });
  148. }
  149. },
  150. "#sort": {
  151. "should sort a single document": function singleValue(next) {
  152. var cwc = new CursorDocumentSource.CursorWithContext();
  153. cwc._cursor = new Cursor( [{_id:0, a: 1}] );
  154. var cds = new CursorDocumentSource(cwc);
  155. var sds = new SortDocumentSource();
  156. sds.addKey("_id", false);
  157. sds.setSource(cds);
  158. sds.getNext(function(err, actual) {
  159. assert.deepEqual(actual, {_id:0, a:1});
  160. next();
  161. });
  162. },
  163. "should sort two documents": function twoValue(next) {
  164. var cwc = new CursorDocumentSource.CursorWithContext();
  165. var l = [{_id:0, a: 1}, {_id:1, a:0}];
  166. cwc._cursor = new Cursor( l );
  167. var cds = new CursorDocumentSource(cwc);
  168. var sds = new SortDocumentSource();
  169. sds.addKey("_id", false);
  170. sds.setSource(cds);
  171. async.series([
  172. sds.getNext.bind(sds),
  173. sds.getNext.bind(sds),
  174. ],
  175. function(err,res) {
  176. assert.deepEqual([{_id:1, a: 0}, {_id:0, a:1}], res);
  177. next();
  178. }
  179. );
  180. },
  181. "should sort two documents in ascending order": function ascendingValue(next) {
  182. var cwc = new CursorDocumentSource.CursorWithContext();
  183. var l = [{_id:0, a: 1}, {_id:5, a:12}, {_id:1, a:0}];
  184. cwc._cursor = new Cursor( l );
  185. var cds = new CursorDocumentSource(cwc);
  186. var sds = new SortDocumentSource();
  187. sds.addKey("_id", true);
  188. sds.setSource(cds);
  189. var docs = [], i = 0;
  190. async.doWhilst(
  191. function(cb) {
  192. sds.getNext(function(err, val) {
  193. docs[i] = val;
  194. return cb(err);
  195. });
  196. },
  197. function() {
  198. return docs[i++] !== DocumentSource.EOF;
  199. },
  200. function(err) {
  201. assert.deepEqual([{_id:0, a: 1}, {_id:1, a:0}, {_id:5, a:12}, DocumentSource.EOF], docs);
  202. next();
  203. }
  204. );
  205. },
  206. "should sort documents with a compound key": function compoundKeySort(next) {
  207. var cwc = new CursorDocumentSource.CursorWithContext();
  208. var l = [{_id:0, a: 1, b:3}, {_id:5, a:12, b:7}, {_id:1, a:0, b:2}];
  209. cwc._cursor = new Cursor( l );
  210. var cds = new CursorDocumentSource(cwc);
  211. var sds = new SortDocumentSource();
  212. sds.addKey("a", false);
  213. sds.addKey("b", false);
  214. sds.setSource(cds);
  215. var docs = [], i = 0;
  216. async.doWhilst(
  217. function(cb) {
  218. sds.getNext(function(err, val) {
  219. docs[i] = val;
  220. return cb(err);
  221. });
  222. },
  223. function() {
  224. return docs[i++] !== DocumentSource.EOF;
  225. },
  226. function(err) {
  227. assert.deepEqual([{_id:5, a:12, b:7}, {_id:0, a:1, b:3}, {_id:1, a:0, b:2}, DocumentSource.EOF], docs);
  228. next();
  229. }
  230. );
  231. },
  232. "should sort documents with a compound key in ascending order": function compoundAscendingKeySort(next) {
  233. var cwc = new CursorDocumentSource.CursorWithContext();
  234. var l = [{_id:0, a: 1, b:3}, {_id:5, a:12, b:7}, {_id:1, a:0, b:2}];
  235. cwc._cursor = new Cursor( l );
  236. var cds = new CursorDocumentSource(cwc);
  237. var sds = new SortDocumentSource();
  238. sds.addKey("a", true);
  239. sds.addKey("b", true);
  240. sds.setSource(cds);
  241. var docs = [], i = 0;
  242. async.doWhilst(
  243. function(cb) {
  244. sds.getNext(function(err, val) {
  245. docs[i] = val;
  246. return cb(err);
  247. });
  248. },
  249. function() {
  250. return docs[i++] !== DocumentSource.EOF;
  251. },
  252. function(err) {
  253. assert.deepEqual([{_id:1, a:0, b:2}, {_id:0, a:1, b:3}, {_id:5, a:12, b:7}, DocumentSource.EOF], docs);
  254. next();
  255. }
  256. );
  257. },
  258. "should sort documents with a compound key in mixed order": function compoundMixedKeySort(next) {
  259. var cwc = new CursorDocumentSource.CursorWithContext();
  260. var l = [{_id:0, a: 1, b:3}, {_id:5, a:12, b:7}, {_id:1, a:0, b:2}, {_id:8, a:7, b:42}];
  261. cwc._cursor = new Cursor( l );
  262. var cds = new CursorDocumentSource(cwc);
  263. var sds = new SortDocumentSource();
  264. sds.addKey("a", true);
  265. sds.addKey("b", false);
  266. sds.setSource(cds);
  267. var docs = [], i = 0;
  268. async.doWhilst(
  269. function(cb) {
  270. sds.getNext(function(err, val) {
  271. docs[i] = val;
  272. return cb(err);
  273. });
  274. },
  275. function() {
  276. return docs[i++] !== DocumentSource.EOF;
  277. },
  278. function(err) {
  279. assert.deepEqual([{_id:1, a:0, b:2}, {_id:0, a:1, b:3}, {_id:8, a:7, b:42}, {_id:5, a:12, b:7}, DocumentSource.EOF], docs);
  280. next();
  281. }
  282. );
  283. },
  284. "should not sort different types": function diffTypesSort() {
  285. var cwc = new CursorDocumentSource.CursorWithContext();
  286. var l = [{_id:0, a: 1}, {_id:1, a:"foo"}];
  287. cwc._cursor = new Cursor( l );
  288. var cds = new CursorDocumentSource(cwc);
  289. var sds = new SortDocumentSource();
  290. sds.addKey("a", false);
  291. assert.throws(sds.setSource(cds));
  292. },
  293. "should sort docs with missing fields": function missingFields(next) {
  294. var cwc = new CursorDocumentSource.CursorWithContext();
  295. var l = [{_id:0, a: 1}, {_id:1}];
  296. cwc._cursor = new Cursor( l );
  297. var cds = new CursorDocumentSource(cwc);
  298. var sds = new SortDocumentSource();
  299. sds.addKey("a", true);
  300. sds.setSource(cds);
  301. var docs = [], i = 0;
  302. async.doWhilst(
  303. function(cb) {
  304. sds.getNext(function(err, val) {
  305. docs[i] = val;
  306. return cb(err);
  307. });
  308. },
  309. function() {
  310. return docs[i++] !== DocumentSource.EOF;
  311. },
  312. function(err) {
  313. assert.deepEqual([{_id:1}, {_id:0, a:1}, DocumentSource.EOF], docs);
  314. next();
  315. }
  316. );
  317. },
  318. "should sort docs with null fields": function nullFields(next) {
  319. var cwc = new CursorDocumentSource.CursorWithContext();
  320. var l = [{_id:0, a: 1}, {_id:1, a: null}];
  321. cwc._cursor = new Cursor( l );
  322. var cds = new CursorDocumentSource(cwc);
  323. var sds = new SortDocumentSource();
  324. sds.addKey("a", true);
  325. sds.setSource(cds);
  326. var docs = [], i = 0;
  327. async.doWhilst(
  328. function(cb) {
  329. sds.getNext(function(err, val) {
  330. docs[i] = val;
  331. return cb(err);
  332. });
  333. },
  334. function() {
  335. return docs[i++] !== DocumentSource.EOF;
  336. },
  337. function(err) {
  338. assert.deepEqual([{_id:1, a:null}, {_id:0, a:1}, DocumentSource.EOF], docs);
  339. next();
  340. }
  341. );
  342. },
  343. "should not support a missing object nested in an array": function missingObjectWithinArray() {
  344. var cwc = new CursorDocumentSource.CursorWithContext();
  345. var l = [{_id:0, a: [1]}, {_id:1, a:[0]}];
  346. cwc._cursor = new Cursor( l );
  347. var cds = new CursorDocumentSource(cwc);
  348. var sds = new SortDocumentSource();
  349. assert.throws(function() {
  350. sds.addKey("a.b", false);
  351. sds.setSource(cds);
  352. var c = [];
  353. while (!sds.eof()) {
  354. c.push(sds.getCurrent());
  355. sds.advance();
  356. }
  357. });
  358. },
  359. "should compare nested values from within an array": function extractArrayValues(next) {
  360. var cwc = new CursorDocumentSource.CursorWithContext();
  361. var l = [{_id:0,a:[{b:1},{b:2}]}, {_id:1,a:[{b:1},{b:1}]} ];
  362. cwc._cursor = new Cursor( l );
  363. var cds = new CursorDocumentSource(cwc);
  364. var sds = new SortDocumentSource();
  365. sds.addKey("a.b", true);
  366. sds.setSource(cds);
  367. var docs = [], i = 0;
  368. async.doWhilst(
  369. function(cb) {
  370. sds.getNext(function(err, val) {
  371. docs[i] = val;
  372. return cb(err);
  373. });
  374. },
  375. function() {
  376. return docs[i++] !== DocumentSource.EOF;
  377. },
  378. function(err) {
  379. assert.deepEqual([{_id:1,a:[{b:1},{b:1}]},{_id:0,a:[{b:1},{b:2}]}, DocumentSource.EOF], docs);
  380. next();
  381. }
  382. );
  383. }
  384. },
  385. "#coalesce()": {
  386. "should return false when coalescing a non-limit source": function nonLimitSource() {
  387. var cwc = new CursorDocumentSource.CursorWithContext();
  388. var l = [{_id:0,a:[{b:1},{b:2}]}, {_id:1,a:[{b:1},{b:1}]} ];
  389. cwc._cursor = new Cursor( l );
  390. var cds = new CursorDocumentSource(cwc),
  391. sds = SortDocumentSource.createFromJson({a:1});
  392. var newSrc = sds.coalesce(cds);
  393. assert.equal(newSrc, false);
  394. },
  395. "should return limit source when coalescing a limit source": function limitSource() {
  396. var sds = SortDocumentSource.createFromJson({a:1}),
  397. lds = LimitDocumentSource.createFromJson(1);
  398. var newSrc = sds.coalesce(LimitDocumentSource.createFromJson(10));
  399. assert.ok(newSrc instanceof LimitDocumentSource);
  400. assert.equal(sds.getLimit(), 10);
  401. assert.equal(newSrc.limit, 10);
  402. sds.coalesce(LimitDocumentSource.createFromJson(5));
  403. assert.equal(sds.getLimit(), 5);
  404. var arr = [];
  405. sds.serializeToArray(arr);
  406. assert.deepEqual(arr, [{$sort: {a:1}}, {$limit: 5}]);
  407. },
  408. },
  409. "#dependencies": {
  410. "should have Dependant field paths": function dependencies() {
  411. var sds = new SortDocumentSource();
  412. sds.addKey("a", true);
  413. sds.addKey("b.c", false);
  414. var deps = {};
  415. assert.equal("SEE_NEXT", sds.getDependencies(deps));
  416. assert.equal(2, Object.keys(deps).length);
  417. assert.ok(deps.a);
  418. assert.ok(deps["b.c"]);
  419. }
  420. }
  421. }
  422. };
  423. if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).grep(process.env.MOCHA_GREP || '').run(process.exit);