Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-17258

Add $reduce expression operator for rollup of arrays

    XMLWordPrintable

    Details

    • Backwards Compatibility:
      Fully Compatible
    • Sprint:
      Query 13 (04/22/16)
    • Case:

      Description

      Syntax

      {
          $reduce: {
              input: <array>,
              initialValue: <expression>,
              in: <expression>
          }
      }
      

      Examples

      > db.coll.insert([
        {_id: 1, array_field: [1,2,3]}
        {_id: 2, array_field: []}
      ]);
      > db.coll.aggregate([{
          $project: {
              total: {
                  $reduce: {  // Use $reduce to compute the sum.
                      input: "$array_field",
                      initialValue: 0,
                      in: {$add : ["$$value", "$$this"]}
                  }
              }
          }
      }])
      {_id: 1, total: 6}
      {_id: 2, total: 0}
       
       
      // Example 2
      > db.coll.insert([
        {_id: 1, array_field: [1,2,3]}
        {_id: 2, array_field: []}
      ]);
      > db.coll.aggregate([{
        $project: {
          array_totals: {
            $reduce: {  // Compute both the sum and the product.
              input: "$array_field",
              initialValue: {sum: 0, product: 1},
              in: {
                sum: {$add : ["$$value.sum", "$$this"]},
                product: {$multiply: ["$$value.product", "$$this"]}
              }
            }
          }
        }
      }])
      {_id: 1, array_totals: {sum: 6, product: 6}}
      {_id: 2, array_totals: {sum: 0, product: 1}}
      

      Notes

      • If input is an empty array, the value given to initialResult is returned.

      Errors

      • If a value from the input expression is anything but a single array value.
      • 'input', 'initialResult', and 'in' are required options.

      Old Description
      We currently have the $map operator to apply an expression to each element in an array and return an array with the applied results.

      Along similar lines, it would also be useful to have a $reduce operator to rollup an array into a scalar element using a specified combining function. For example, to return the sum of all array elements.

      To illustrate:

      Given an input of:

      { _id:1, array_field: [1,2,3] }
      { _id:2, array_field: [] }
      

      We should return:

      { _id:1, array_total: 6 }
      { _id:2, array_total: 0 }
      

      This is currently possible using a relatively expensive $unwind and $group (plus some additionally voodoo to handle unwinding of the empty arrays), but it would be more elegant, and likely more performant, to do something like the following:

      db.coll.aggregate(
         [
            { $project:
               { array_total:
                  {
                    $reduce:
                       {
                         $add: "$array_field",
                       }
                  }
               }
            }
         ]
      )
      

      [Note: the above proposed syntax is just for illustrative purposes]

        Attachments

          Issue Links

            Activity

              People

              • Votes:
                9 Vote for this issue
                Watchers:
                15 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: