aggregate.js 5.7 KB

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