Definition
$reduceApplies an expression to each element in an array and combines them into a single value.
$reducehas the following syntax:{ $reduce: { input: <array>, initialValue: <expression>, in: <expression>, as: <string>, valueAs: <string>, arrayIndexAs: <string> } } FieldTypeDescriptioninputarray
Can be any valid expression that resolves to an array. For more information on expressions, see Expressions.
If the argument resolves to a value of
nullor refers to a missing field,$reducereturnsnull.If the argument does not resolve to an array or
nullnor refers to a missing field,$reducereturns an error.initialValueexpression
The initial cumulative
valueset beforeinis applied to the first element of theinputarray.inexpression
A valid expression that
$reduceapplies to each element in theinputarray in left-to-right order. Wrap theinputvalue with$reverseArrayto yield the equivalent of applying the combining expression from right-to-left.During evaluation of the
inexpression, two variables will be available:asstring
Optional. A name for the variable that represents each individual element of the
inputarray. If omitted, the variable name defaults tothis.New in version 8.3.
valueAsstring
Optional. A name for the variable that represents the cumulative value of the expression. If omitted, the variable name defaults to
value.New in version 8.3.
arrayIndexAsstring
Optional. A name for the aggregation variable that represents the index of the current element in the
inputarray. The first array element index is0.You can use the variable name in an expression. For example, if you specify
arrayIndexAs: "myIndex", you use$$myIndexin the expression.$$myIndexreturns the index of the current element in theinputarray.If you omit
arrayIndexAs, you can use the$$IDXsystem variable in the expression to return the index of the current element.For examples, see Access the Index of Each Item in an Array and Use
$$IDXto Access the Index.New in version 8.3.
If
inputresolves to an empty array,$reducereturnsinitialValue.
Example | Results | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
| ||||||||||
|
| ||||||||||
|
|
Examples
Multiplication
Probability
A collection named events contains the events of a probability
experiment. Each experiment can have multiple events, such as
rolling a die several times or drawing several cards (without replacement)
in succession to achieve a desired result. In order to obtain the
overall probability of the experiment, we will need to multiply the
probability of each event in the experiment.
db.events.insertMany( [ { _id : 1, type : "die", experimentId :"r5", description : "Roll a 5", eventNum : 1, probability : 0.16666666666667 }, { _id : 2, type : "card", experimentId :"d3rc", description : "Draw 3 red cards", eventNum : 1, probability : 0.5 }, { _id : 3, type : "card", experimentId :"d3rc", description : "Draw 3 red cards", eventNum : 2, probability : 0.49019607843137 }, { _id : 4, type : "card", experimentId :"d3rc", description : "Draw 3 red cards", eventNum : 3, probability : 0.48 }, { _id : 5, type : "die", experimentId :"r16", description : "Roll a 1 then a 6", eventNum : 1, probability : 0.16666666666667 }, { _id : 6, type : "die", experimentId :"r16", description : "Roll a 1 then a 6", eventNum : 2, probability : 0.16666666666667 }, { _id : 7, type : "card", experimentId :"dak", description : "Draw an ace, then a king", eventNum : 1, probability : 0.07692307692308 }, { _id : 8, type : "card", experimentId :"dak", description : "Draw an ace, then a king", eventNum : 2, probability : 0.07843137254902 } ] )
Steps:
Use
$groupto group by theexperimentIdand use$pushto create an array with the probability of each event.Use
$reducewith$multiplyto multiply and combine the elements ofprobabilityArrinto a single value and project it.
db.probability.aggregate( [ { $group: { _id: "$experimentId", probabilityArr: { $push: "$probability" } } }, { $project: { description: 1, results: { $reduce: { input: "$probabilityArr", initialValue: 1, in: { $multiply: [ "$$value", "$$this" ] } } } } } ] )
The operation returns the following:
{ _id : "dak", results : 0.00603318250377101 } { _id : "r5", results : 0.16666666666667 } { _id : "r16", results : 0.027777777777778886 } { _id : "d3rc", results : 0.11764705882352879 }
Discounted Merchandise
A collection named clothes contains the following documents:
db.clothes.insertMany( [ { _id : 1, productId : "ts1", description : "T-Shirt", color : "black", size : "M", price : 20, discounts : [ 0.5, 0.1 ] }, { _id : 2, productId : "j1", description : "Jeans", color : "blue", size : "36", price : 40, discounts : [ 0.25, 0.15, 0.05 ] }, { _id : 3, productId : "s1", description : "Shorts", color : "beige", size : "32", price : 30, discounts : [ 0.15, 0.05 ] }, { _id : 4, productId : "ts2", description : "Cool T-Shirt", color : "White", size : "L", price : 25, discounts : [ 0.3 ] }, { _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 currently
available percent-off coupons for each item. If each discount can be
applied to the product once, we can calculate the lowest price by using
$reduce to apply the following formula for each element in the
discounts array: (1 - discount) * price.
db.clothes.aggregate( [ { $project: { discountedPrice: { $reduce: { input: "$discounts", initialValue: "$price", in: { $multiply: [ "$$value", { $subtract: [ 1, "$$this" ] } ] } } } } } ] )
The operation returns the following:
{ _id : ObjectId("57c893067054e6e47674ce01"), discountedPrice : 9 } { _id : ObjectId("57c9932b7054e6e47674ce12"), discountedPrice : 24.224999999999998 } { _id : ObjectId("57c993457054e6e47674ce13"), discountedPrice : 24.224999999999998 } { _id : ObjectId("57c993687054e6e47674ce14"), discountedPrice : 17.5 } { _id : ObjectId("57c993837054e6e47674ce15"), discountedPrice : 54 }
String Concatenation
A collection named people contains the following documents:
db.people.insertMany( [ { _id : 1, name : "Melissa", hobbies : [ "softball", "drawing", "reading" ] }, { _id : 2, name : "Brad", hobbies : [ "gaming", "skateboarding" ] }, { _id : 3, name : "Scott", hobbies : [ "basketball", "music", "fishing" ] }, { _id : 4, name : "Tracey", hobbies : [ "acting", "yoga" ] }, { _id : 5, name : "Josh", hobbies : [ "programming" ] }, { _id : 6, name : "Claire" } ] )
The following example reduces the hobbies array of strings into a single string
bio:
db.people.aggregate( [ // Filter to return only non-empty arrays { $match: { "hobbies": { $gt: [ ] } } }, { $project: { name: 1, bio: { $reduce: { input: "$hobbies", initialValue: "My hobbies include:", in: { $concat: [ "$$value", { $cond: { if: { $eq: [ "$$value", "My hobbies include:" ] }, then: " ", else: ", " } }, "$$this" ] } } } } } ] )
The operation returns the following:
{ _id : 1, name : "Melissa", bio : "My hobbies include: softball, drawing, reading" } { _id : 2, name : "Brad", bio : "My hobbies include: gaming, skateboarding" } { _id : 3, name : "Scott", bio : "My hobbies include: basketball, music, fishing" } { _id : 4, name : "Tracey", bio : "My hobbies include: acting, yoga" } { _id : 5, name : "Josh", bio : "My hobbies include: programming" }
Array Concatenation
A collection named matrices contains the following documents:
db.matrices.insertMany( [ { _id : 1, arr : [ [ 24, 55, 79 ], [ 14, 78, 35 ], [ 84, 90, 3 ], [ 50, 89, 70 ] ] }, { _id : 2, arr : [ [ 39, 32, 43, 7 ], [ 62, 17, 80, 64 ], [ 17, 88, 11, 73 ] ] }, { _id : 3, arr : [ [ 42 ], [ 26, 59 ], [ 17 ], [ 72, 19, 35 ] ] }, { _id : 4 } ] )
Computing a Single Reduction
The following example collapses the two dimensional arrays into a single array collapsed:
db.arrayconcat.aggregate( [ { $project: { collapsed: { $reduce: { input: "$arr", initialValue: [ ], in: { $concatArrays: [ "$$value", "$$this" ] } } } } } ] )
The operation returns the following:
{ _id : 1, collapsed : [ 24, 55, 79, 14, 78, 35, 84, 90, 3, 50, 89, 70 ] } { _id : 2, collapsed : [ 39, 32, 43, 7, 62, 17, 80, 64, 17, 88, 11, 73 ] } { _id : 3, collapsed : [ 42, 26, 59, 17, 72, 19, 35 ] } { _id : 4, collapsed : null }
Computing a Multiple Reductions
The following example performs the same two dimensional array collapse as the example above, but also creates a new array containing only the first element of each array.
db.arrayconcat.aggregate( [ { $project: { results: { $reduce: { input: "$arr", initialValue: [ ], in: { collapsed: { $concatArrays: [ "$$value.collapsed", "$$this" ] }, firstValues: { $concatArrays: [ "$$value.firstValues", { $slice: [ "$$this", 1 ] } ] } } } } } } ] )
The operation returns the following:
{ _id : 1, results : { collapsed : [ 24, 55, 79, 14, 78, 35, 84, 90, 3, 50, 89, 70 ], firstValues : [ 24, 14, 84, 50 ] } } { _id : 2, results : { collapsed : [ 39, 32, 43, 7, 62, 17, 80, 64, 17, 88, 11, 73 ], firstValues : [ 39, 62, 17 ] } } { _id : 3, results : { collapsed : [ 42, 26, 59, 17, 72, 19, 35 ], firstValues : [ 42, 26, 17, 72 ] } } { _id : 4, results : null }
Access the Index of Each Item in an Array
Create a sample collection named people with these documents:
db.people.insertMany( [ { _id: 1, name: "Melissa", hobbies: [ "softball", "drawing", "reading" ] }, { _id: 2, name: "Brad", hobbies: [ "gaming", "skateboarding" ] }, { _id: 3, name: "Scott", hobbies: [ "basketball", "music", "fishing" ] }, { _id: 4, name: "Tracey", hobbies: [ "acting", "yoga" ] }, { _id: 5, name: "Josh", hobbies: [ "programming" ] }, { _id: 6, name: "Claire" } ] )
The hobbies field contains an array of each person's hobbies in
ranked order. The first hobby in the array is the person's primary hobby
that the person spends the most time on. The first hobby has an array
index of 0.
The following example uses valueAs, as, and arrayIndexAs.
The myIndex variable has the index of each hobby in the hobbies
array. The example returns documents with these fields:
Person name.
textfield, which includes the hobby name and the position of each hobby in the list.
db.people.aggregate( [ { $project: { _id: 0, name: 1, text: { $reduce: { input: "$hobbies", initialValue: "My hobbies include:", valueAs: "accumulatedText", as: "hobby", arrayIndexAs: "myIndex", in: { $concat: [ "$$accumulatedText", " ", { $toString: { $add: [ "$$myIndex", 1 ] } }, ") ", "$$hobby" ] } } } } } ] )
Output:
[ { "name" : "Melissa", "text" : "My hobbies include: 1) softball 2) drawing 3) reading" } { "name" : "Brad", "text" : "My hobbies include: 1) gaming 2) skateboarding" } { "name" : "Scott", "text" : "My hobbies include: 1) basketball 2) music 3) fishing" } { "name" : "Tracey", "text" : "My hobbies include: 1) acting 2) yoga" } { "name" : "Josh", "text" : "My hobbies include: 1) programming" } { "name" : "Claire", "text" : null } ]
Use $$IDX to Access the Index
The $$IDX variable returns the index of the current
element in the input array. You can use $$IDX if you omit the
arrayIndexAs field from the expression.
The following example returns the same documents as the example in the
previous section Access the Index of Each Item in an Array, but uses $$IDX instead
of arrayIndexAs:
db.people.aggregate( [ { $project: { _id: 0, name: 1, text: { $reduce: { input: "$hobbies", initialValue: "My hobbies include:", valueAs: "accumulatedText", as: "hobby", in: { $concat: [ "$$accumulatedText", " ", { $toString: { $add: [ "$$IDX", 1 ] } }, ") ", "$$hobby" ] } } } } } ] )