$reduce (aggregation)

Definition

  • $reduce

New in version 3.4.

Applies an expression to each element in an array and combines theminto a single value.

$reduce has the following syntax:

  1. {
  2. $reduce: {
  3. input: <array>,
  4. initialValue: <expression>,
  5. in: <expression>
  6. }
  7. }

FieldTypeDescriptioninputarrayCan be any valid expressionthat resolves to an array. For more information onexpressions, see Expressions.

If the argument resolves to a value of null or refers to amissing field, $reduce returns null.

If the argument does not resolve to an array or null nor refersto a missing field, $reduce returns an error.initialValueexpressionThe initial cumulative value set before in is applied to the first elementof the input array.inexpressionA valid expression that $reduce appliesto each element in the input array in left-to-right order. Wrap the inputvalue with $reverseArray to yield the equivalent of applyingthe combining expression from right-to-left.

During evaluation of the in expression, two variables will be available:

  • value is the variablethat represents the cumulative value of the expression.
  • this is the variablethat refers to the element being processed.

If input resolves to an empty array, $reduce returnsinitialValue.

ExampleResults
  1. { $reduce: { input: ["a", "b", "c"], initialValue: "", in: { $concat : ["$$value", "$$this"] } } }
"abc"
  1. { $reduce: { input: [ 1, 2, 3, 4 ], initialValue: { sum: 5, product: 2 }, in: { sum: { $add : ["$$value.sum", "$$this"] }, product: { $multiply: [ "$$value.product", "$$this" ] } } }}
{ "sum" : 15, "product" : 48 }
  1. { $reduce: { input: [ [ 3, 4 ], [ 5, 6 ] ], initialValue: [ 1, 2 ], in: { $concatArrays : ["$$value", "$$this"] } }}
[ 1, 2, 3, 4, 5, 6 ]

Examples

Multiplication

Probability

A collection named events contains the events of a probabilityexperiment. Each experiment can have multiple events, such asrolling a die several times or drawing several cards (without replacement)in succession to achieve a desired result. In order to obtain theoverall probability of the experiment, we will need to multiply theprobability of each event in the experiment.

  1. {_id:1, "type":"die", "experimentId":"r5", "description":"Roll a 5", "eventNum":1, "probability":0.16666666666667}
  2. {_id:2, "type":"card", "experimentId":"d3rc", "description":"Draw 3 red cards", "eventNum":1, "probability":0.5}
  3. {_id:3, "type":"card", "experimentId":"d3rc", "description":"Draw 3 red cards", "eventNum":2, "probability":0.49019607843137}
  4. {_id:4, "type":"card", "experimentId":"d3rc", "description":"Draw 3 red cards", "eventNum":3, "probability":0.48}
  5. {_id:5, "type":"die", "experimentId":"r16", "description":"Roll a 1 then a 6", "eventNum":1, "probability":0.16666666666667}
  6. {_id:6, "type":"die", "experimentId":"r16", "description":"Roll a 1 then a 6", "eventNum":2, "probability":0.16666666666667}
  7. {_id:7, "type":"card", "experimentId":"dak", "description":"Draw an ace, then a king", "eventNum":1, "probability":0.07692307692308}
  8. {_id:8, "type":"card", "experimentId":"dak", "description":"Draw an ace, then a king", "eventNum":2, "probability":0.07843137254902}

Steps:

  • Use $group to group by the experimentId and use $push tocreate an array with the probability of each event.
  • Use $reduce with $multiply to multiply and combine theelements of probabilityArr into a single value and project it.
  1. db.probability.aggregate(
  2. [
  3. {
  4. $group: {
  5. _id: "$experimentId",
  6. "probabilityArr": { $push: "$probability" }
  7. }
  8. },
  9. {
  10. $project: {
  11. "description": 1,
  12. "results": {
  13. $reduce: {
  14. input: "$probabilityArr",
  15. initialValue: 1,
  16. in: { $multiply: [ "$$value", "$$this" ] }
  17. }
  18. }
  19. }
  20. }
  21. ]
  22. )

The operation returns the following:

  1. { "_id" : "dak", "results" : 0.00603318250377101 }
  2. { "_id" : "r5", "results" : 0.16666666666667 }
  3. { "_id" : "r16", "results" : 0.027777777777778886 }
  4. { "_id" : "d3rc", "results" : 0.11764705882352879 }

Discounted Merchandise

A collection named clothes contains the following documents:

  1. { "_id" : 1, "productId" : "ts1", "description" : "T-Shirt", "color" : "black", "size" : "M", "price" : 20, "discounts" : [ 0.5, 0.1 ] }
  2. { "_id" : 2, "productId" : "j1", "description" : "Jeans", "color" : "blue", "size" : "36", "price" : 40, "discounts" : [ 0.25, 0.15, 0.05 ] }
  3. { "_id" : 3, "productId" : "s1", "description" : "Shorts", "color" : "beige", "size" : "32", "price" : 30, "discounts" : [ 0.15, 0.05 ] }
  4. { "_id" : 4, "productId" : "ts2", "description" : "Cool T-Shirt", "color" : "White", "size" : "L", "price" : 25, "discounts" : [ 0.3 ] }
  5. { "_id" : 5, "productId" : "j2", "description" : "Designer Jeans", "color" : "blue", "size" : "30", "price" : 80, "discounts" : [ 0.1, 0.25 ] }

Each document contains a discounts array containing the currentlyavailable percent-off coupons for each item. If each discount can beapplied to the product once, we can calculate the lowest price by using$reduce to apply the following formula for each element in thediscounts array: (1 - discount) * price.

  1. db.clothes.aggregate(
  2. [
  3. {
  4. $project: {
  5. "discountedPrice": {
  6. $reduce: {
  7. input: "$discounts",
  8. initialValue: "$price",
  9. in: { $multiply: [ "$$value", { $subtract: [ 1, "$$this" ] } ] }
  10. }
  11. }
  12. }
  13. }
  14. ]
  15. )

The operation returns the following:

  1. { "_id" : ObjectId("57c893067054e6e47674ce01"), "discountedPrice" : 9 }
  2. { "_id" : ObjectId("57c9932b7054e6e47674ce12"), "discountedPrice" : 24.224999999999998 }
  3. { "_id" : ObjectId("57c993457054e6e47674ce13"), "discountedPrice" : 24.224999999999998 }
  4. { "_id" : ObjectId("57c993687054e6e47674ce14"), "discountedPrice" : 17.5 }
  5. { "_id" : ObjectId("57c993837054e6e47674ce15"), "discountedPrice" : 54 }

String Concatenation

A collection named people contains the following documents:

  1. { "_id" : 1, "name" : "Melissa", "hobbies" : [ "softball", "drawing", "reading" ] }
  2. { "_id" : 2, "name" : "Brad", "hobbies" : [ "gaming", "skateboarding" ] }
  3. { "_id" : 3, "name" : "Scott", "hobbies" : [ "basketball", "music", "fishing" ] }
  4. { "_id" : 4, "name" : "Tracey", "hobbies" : [ "acting", "yoga" ] }
  5. { "_id" : 5, "name" : "Josh", "hobbies" : [ "programming" ] }
  6. { "_id" : 6, "name" : "Claire" }

The following example reduces the hobbies array of strings into a single stringbio:

  1. db.people.aggregate(
  2. [
  3. // Filter to return only non-empty arrays
  4. { $match: { "hobbies": { $gt: [ ] } } },
  5. {
  6. $project: {
  7. "name": 1,
  8. "bio": {
  9. $reduce: {
  10. input: "$hobbies",
  11. initialValue: "My hobbies include:",
  12. in: {
  13. $concat: [
  14. "$$value",
  15. {
  16. $cond: {
  17. if: { $eq: [ "$$value", "My hobbies include:" ] },
  18. then: " ",
  19. else: ", "
  20. }
  21. },
  22. "$$this"
  23. ]
  24. }
  25. }
  26. }
  27. }
  28. }
  29. ]
  30. )

The operation returns the following:

  1. { "_id" : 1, "name" : "Melissa", "bio" : "My hobbies include: softball, drawing, reading" }
  2. { "_id" : 2, "name" : "Brad", "bio" : "My hobbies include: gaming, skateboarding" }
  3. { "_id" : 3, "name" : "Scott", "bio" : "My hobbies include: basketball, music, fishing" }
  4. { "_id" : 4, "name" : "Tracey", "bio" : "My hobbies include: acting, yoga" }
  5. { "_id" : 5, "name" : "Josh", "bio" : "My hobbies include: programming" }

Array Concatenation

A collection named matrices contains the following documents:

  1. { "_id" : 1, "arr" : [ [ 24, 55, 79 ], [ 14, 78, 35 ], [ 84, 90, 3 ], [ 50, 89, 70 ] ] }
  2. { "_id" : 2, "arr" : [ [ 39, 32, 43, 7 ], [ 62, 17, 80, 64 ], [ 17, 88, 11, 73 ] ] }
  3. { "_id" : 3, "arr" : [ [ 42 ], [ 26, 59 ], [ 17 ], [ 72, 19, 35 ] ] }
  4. { "_id" : 4 }

Computing a Single Reduction

The following example collapses the two dimensional arrays into a single array collapsed:

  1. db.arrayconcat.aggregate(
  2. [
  3. {
  4. $project: {
  5. "collapsed": {
  6. $reduce: {
  7. input: "$arr",
  8. initialValue: [ ],
  9. in: { $concatArrays: [ "$$value", "$$this" ] }
  10. }
  11. }
  12. }
  13. }
  14. ]
  15. )

The operation returns the following:

  1. { "_id" : 1, "collapsed" : [ 24, 55, 79, 14, 78, 35, 84, 90, 3, 50, 89, 70 ] }
  2. { "_id" : 2, "collapsed" : [ 39, 32, 43, 7, 62, 17, 80, 64, 17, 88, 11, 73 ] }
  3. { "_id" : 3, "collapsed" : [ 42, 26, 59, 17, 72, 19, 35 ] }
  4. { "_id" : 4, "collapsed" : null }

Computing a Multiple Reductions

The following example performs the same two dimensional array collapse as the example above, but alsocreates a new array containing only the first element of each array.

  1. db.arrayconcat.aggregate(
  2. [
  3. {
  4. $project: {
  5. "results": {
  6. $reduce: {
  7. input: "$arr",
  8. initialValue: [ ],
  9. in: {
  10. "collapsed": {
  11. $concatArrays: [ "$$value.collapsed", "$$this" ]
  12. },
  13. "firstValues": {
  14. $concatArrays: [ "$$value.firstValues", { $slice: [ "$$this", 1 ] } ]
  15. }
  16. }
  17. }
  18. }
  19. }
  20. }
  21. ]
  22. )

The operation returns the following:

  1. { "_id" : 1, "results" : { "collapsed" : [ 24, 55, 79, 14, 78, 35, 84, 90, 3, 50, 89, 70 ], "firstValues" : [ 24, 14, 84, 50 ] } }
  2. { "_id" : 2, "results" : { "collapsed" : [ 39, 32, 43, 7, 62, 17, 80, 64, 17, 88, 11, 73 ], "firstValues" : [ 39, 62, 17 ] } }
  3. { "_id" : 3, "results" : { "collapsed" : [ 42, 26, 59, 17, 72, 19, 35 ], "firstValues" : [ 42, 26, 17, 72 ] } }
  4. { "_id" : 4, "results" : null }