aggregate.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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. testAggregate({
  101. inputs: [{_id:0, e:1, f:23}, {_id:2, e:2, g:34}, {_id:4, e:3}],
  102. pipeline: [
  103. {$project:{
  104. e:1,
  105. a:{$add:["$e", "$e"]},
  106. b:{$cond:[{$eq:["$e", 2]}, "two", "not two"]}
  107. //TODO: high level test of all other expression operators
  108. }}
  109. ],
  110. 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"}],
  111. next: next
  112. });
  113. },
  114. "should be able to use a $project operator to exclude the _id field": function(next){
  115. testAggregate({
  116. inputs: [{_id:0, e:1, f:23}, {_id:2, e:2, g:34}, {_id:4, e:3}],
  117. pipeline: [
  118. {$project:{
  119. _id:0,
  120. e:1
  121. //TODO: high level test of all other expression operators
  122. }}
  123. ],
  124. expected: [{e:1}, {e:2}, {e:3}],
  125. next: next
  126. });
  127. },
  128. "should be able to construct an instance with $sort operators properly (ascending)": function(next){
  129. testAggregate({
  130. inputs: [
  131. {_id:3.14159}, {_id:-273.15},
  132. {_id:42}, {_id:11}, {_id:1},
  133. {_id:null}, {_id:NaN}
  134. ],
  135. pipeline: [{$sort:{_id:1}}],
  136. expected: [
  137. {_id:null}, {_id:NaN},
  138. {_id:-273.15}, {_id:1}, {_id:3.14159}, {_id:11}, {_id:42}
  139. ],
  140. next: next
  141. });
  142. },
  143. "should be able to construct an instance with $group operators properly": function(next){
  144. testAggregate({
  145. inputs: [
  146. {_id:0, a:1},
  147. {_id:0, a:2},
  148. {_id:0, a:3},
  149. {_id:0, a:4},
  150. {_id:0, a:1.5},
  151. {_id:0, a:null},
  152. {_id:1, b:"a"},
  153. {_id:1, b:"b"},
  154. {_id:1, b:"b"},
  155. {_id:1, b:"c"}
  156. ],
  157. pipeline:[
  158. {$group:{
  159. _id:"$_id",
  160. sum_a:{$sum:"$a"},
  161. //min_a:{$min:"$a"}, //this is busted in this version of mongo
  162. max_a:{$max:"$a"},
  163. avg_a:{$avg:"$a"},
  164. first_b:{$first:"$b"},
  165. last_b:{$last:"$b"},
  166. addToSet_b:{$addToSet:"$b"},
  167. push_b:{$push:"$b"}
  168. }}
  169. ],
  170. expected: [
  171. {
  172. _id:0,
  173. sum_a:11.5,
  174. //min_a:1,
  175. max_a:4,
  176. avg_a:2.3,
  177. first_b:undefined,
  178. last_b:undefined,
  179. addToSet_b:[],
  180. push_b:[]
  181. },
  182. {
  183. _id:1,
  184. sum_a:0,
  185. //min_a:null,
  186. max_a:undefined,
  187. avg_a:0,
  188. first_b:"a",
  189. last_b:"c",
  190. addToSet_b:["a", "b", "c"],
  191. push_b:["a", "b", "b", "c"]
  192. }
  193. ],
  194. next: next
  195. });
  196. },
  197. "should be able to construct an instance with $group using concat": function(next){
  198. testAggregate({
  199. inputs: [
  200. {_id:0, a:null},
  201. {_id:1, a:"a"},
  202. {_id:1, a:"b"},
  203. {_id:1, a:"b"},
  204. {_id:1, a:"c"}
  205. ],
  206. pipeline: [
  207. {$group:{
  208. _id:{$concat:["$a"]}
  209. }}
  210. ],
  211. expected: [
  212. {_id: null},
  213. {_id: "a"},
  214. {_id: "b"},
  215. {_id: "c"}
  216. ],
  217. next: next
  218. });
  219. }
  220. }
  221. };
  222. if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).grep(process.env.MOCHA_GREP || '').run(process.exit);