aggregate.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. "use strict";
  2. var assert = require("assert"),
  3. aggregate = require("../../");
  4. // Utility to test the various use cases of `aggregate`
  5. function testAggregate(opts){
  6. // SYNC: test one-off usage
  7. var results = aggregate(opts.pipeline, opts.inputs);
  8. assert.equal(JSON.stringify(results), JSON.stringify(opts.expected));
  9. // SYNC: test reusable aggregator functionality
  10. var aggregator = aggregate(opts.pipeline);
  11. results = aggregator(opts.inputs);
  12. assert.equal(JSON.stringify(results), JSON.stringify(opts.expected));
  13. // SYNC: test that it is actually reusable
  14. results = aggregator(opts.inputs);
  15. assert.equal(JSON.stringify(results), JSON.stringify(opts.expected), "Reuse of aggregator should yield the same results!");
  16. // ASYNC: test one-off usage
  17. aggregate(opts.pipeline, opts.inputs, function(err, results){
  18. assert.ifError(err);
  19. assert.equal(JSON.stringify(results), JSON.stringify(opts.expected));
  20. // ASYNC: test reusable aggregator functionality
  21. var aggregator = aggregate(opts.pipeline);
  22. aggregator(opts.inputs, function(err, results){
  23. assert.ifError(err);
  24. assert.equal(JSON.stringify(results), JSON.stringify(opts.expected));
  25. // ASYNC: test that it is actually reusable
  26. aggregator(opts.inputs, function(err, results){
  27. assert.ifError(err);
  28. assert.equal(JSON.stringify(results), JSON.stringify(opts.expected), "Reuse of aggregator should yield the same results!");
  29. // success!
  30. return opts.next();
  31. });
  32. });
  33. });
  34. }
  35. module.exports = {
  36. "aggregate": {
  37. "should be able to use an empty pipeline (no-op)": function(next){
  38. testAggregate({
  39. inputs: [1, 2, 3],
  40. pipeline: [],
  41. expected: [1, 2, 3],
  42. next: next
  43. });
  44. },
  45. "should be able to use a limit operator": function(next){
  46. testAggregate({
  47. inputs: [{_id:0}, {_id:1}, {_id:2}, {_id:3}, {_id:4}, {_id:5}],
  48. pipeline: [{$limit:2}],
  49. expected: [{_id:0}, {_id:1}],
  50. next: next
  51. });
  52. },
  53. "should be able to use a match operator": function(next){
  54. testAggregate({
  55. inputs: [{_id:0, e:1}, {_id:1, e:0}, {_id:2, e:1}, {_id:3, e:0}, {_id:4, e:1}, {_id:5, e:0}],
  56. pipeline: [{$match:{e:1}}],
  57. expected: [{_id:0, e:1}, {_id:2, e:1}, {_id:4, e:1}],
  58. next: next
  59. });
  60. },
  61. "should be able to use a skip operator": function(next){
  62. testAggregate({
  63. inputs: [{_id:0}, {_id:1}, {_id:2}, {_id:3}, {_id:4}, {_id:5}],
  64. pipeline: [{$skip:2}, {$skip:1}], //testing w/ 2 ensures independent state variables
  65. expected: [{_id:3}, {_id:4}, {_id:5}],
  66. next: next
  67. });
  68. },
  69. "should be able to use a skip and then a limit operator together in the same pipeline": function(next){
  70. testAggregate({
  71. inputs: [{_id:0, e:1}, {_id:1, e:0}, {_id:2, e:1}, {_id:3, e:0}, {_id:4, e:1}, {_id:5, e:0}],
  72. pipeline: [{$skip:2}, {$limit:1}],
  73. expected: [{_id:2, e:1}],
  74. next: next
  75. });
  76. },
  77. "should be able to construct an instance with unwind operators properly": function(next){
  78. testAggregate({
  79. inputs: [
  80. {_id:0, nodes:[
  81. {one:[11], two:[2,2]},
  82. {one:[1,1], two:[22]}
  83. ]},
  84. {_id:1, nodes:[
  85. {two:[22], three:[333]},
  86. {one:[1], three:[3,3,3]}
  87. ]}
  88. ],
  89. pipeline: [{$unwind:"$nodes"}, {$unwind:"$nodes.two"}],
  90. expected: [
  91. {_id:0,nodes:{one:[11],two:2}},
  92. {_id:0,nodes:{one:[11],two:2}},
  93. {_id:0,nodes:{one:[1,1],two:22}},
  94. {_id:1,nodes:{two:22,three:[333]}}
  95. ],
  96. next: next
  97. });
  98. },
  99. "should be able to use a project operator": function(next){
  100. // NOTE: Test case broken until expression is fixed
  101. testAggregate({
  102. inputs: [{_id:0, e:1, f:23}, {_id:2, e:2, g:34}, {_id:4, e:3}],
  103. pipeline: [
  104. {$project:{
  105. e:1,
  106. a:{$add:["$e", "$e"]},
  107. b:{$cond:[{$eq:["$e", 2]}, "two", "not two"]}
  108. //TODO: high level test of all other expression operators
  109. }}
  110. ],
  111. expected: [{_id:0, e:1, a:2, b:"not two"}, {_id:2, e:2, a:4, b:"two"}, {_id:4, e:3, a:6, b:"not two"}],
  112. next: next
  113. });
  114. },
  115. "should be able to use a project operator to exclude the _id field": function(next){
  116. // NOTE: Test case broken until expression is fixed
  117. testAggregate({
  118. inputs: [{_id:0, e:1, f:23}, {_id:2, e:2, g:34}, {_id:4, e:3}],
  119. pipeline: [
  120. {$project:{
  121. _id:0,
  122. e:1
  123. //TODO: high level test of all other expression operators
  124. }}
  125. ],
  126. expected: [{e:1}, {e:2}, {e:3}],
  127. next: next
  128. });
  129. },
  130. "should be able to project out a whole document and leave an empty": function(next) {
  131. testAggregate({
  132. inputs: [{_id:0, a:1}, {_id:1, a:2, b:1}, {_id:2, b:2, c:1}],
  133. pipeline: [
  134. {$project:{
  135. _id:0,
  136. a:1
  137. //TODO: high level test of all other expression operators
  138. }}
  139. ],
  140. expected: [{a:1}, {a:2}, {}],
  141. next: next
  142. });
  143. },
  144. "should be able to construct an instance with sort operators properly (ascending)": function(next){
  145. testAggregate({
  146. inputs: [
  147. {_id:3.14159}, {_id:-273.15},
  148. {_id:42}, {_id:11}, {_id:1},
  149. {_id:null}, {_id:NaN}
  150. ],
  151. pipeline: [{$sort:{_id:1}}],
  152. expected: [
  153. {_id:null}, {_id:NaN},
  154. {_id:-273.15}, {_id:1}, {_id:3.14159}, {_id:11}, {_id:42}
  155. ],
  156. next: next
  157. });
  158. },
  159. "should be able to construct an instance with $group operators properly": function(next){
  160. // NOTE: Test case broken until expression is fixed
  161. testAggregate({
  162. inputs: [
  163. {_id:0, a:1},
  164. {_id:0, a:2},
  165. {_id:0, a:3},
  166. {_id:0, a:4},
  167. {_id:0, a:1.5},
  168. {_id:0, a:null},
  169. {_id:1, b:"a"},
  170. {_id:1, b:"b"},
  171. {_id:1, b:"b"},
  172. {_id:1, b:"c"}
  173. ],
  174. pipeline:[
  175. {$group:{
  176. _id:"$_id",
  177. sum_a:{$sum:"$a"},
  178. //min_a:{$min:"$a"}, //this is busted in this version of mongo
  179. max_a:{$max:"$a"},
  180. avg_a:{$avg:"$a"},
  181. first_b:{$first:"$b"},
  182. last_b:{$last:"$b"},
  183. addToSet_b:{$addToSet:"$b"},
  184. push_b:{$push:"$b"}
  185. }}
  186. ],
  187. expected: [
  188. {
  189. _id:0,
  190. sum_a:11.5,
  191. //min_a:1,
  192. max_a:4,
  193. avg_a:2.3,
  194. first_b:undefined,
  195. last_b:undefined,
  196. addToSet_b:[],
  197. push_b:[]
  198. },
  199. {
  200. _id:1,
  201. sum_a:0,
  202. //min_a:null,
  203. max_a:undefined,
  204. avg_a:0,
  205. first_b:"a",
  206. last_b:"c",
  207. addToSet_b:["a", "b", "c"],
  208. push_b:["a", "b", "b", "c"]
  209. }
  210. ],
  211. next: next
  212. });
  213. },
  214. "should be able to construct an instance with $group using concat": function(next){
  215. // NOTE: Test case broken until expression is fixed; not sure if that concat is supposed to work
  216. testAggregate({
  217. inputs: [
  218. {_id:0, a:null},
  219. {_id:1, a:"a"},
  220. {_id:1, a:"b"},
  221. {_id:1, a:"b"},
  222. {_id:1, a:"c"}
  223. ],
  224. pipeline: [
  225. {$group:{
  226. _id:{$concat:["$a"]}
  227. }}
  228. ],
  229. expected: [
  230. {_id: null},
  231. {_id: "a"},
  232. {_id: "b"},
  233. {_id: "c"}
  234. ],
  235. next: next
  236. });
  237. }
  238. }
  239. };
  240. if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).grep(process.env.MOCHA_GREP || '').run(process.exit);