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

Performance issue for $reduce vs. $map

    • Query Optimization 2021-05-03, Query Optimization 2021-05-17

      I have an array field where I need to replace consecutive values by null, e.g.

      { x: [22, 22, 80, 80, 80, 80, 80, 443, 443, 443, 5223, 5224] }

      should be translated to:

      { x: [22, null, 80, null, null, null, null, 443, null, null, 5223, 5224] }

      I developed two aggregation operations:

      {
         $set: {
            x: {
               $cond: {
                  if: { $isArray: "$x" },
                  then: {
                     $map: {
                        input: { $range: [0, { $size: "$x" }] },
                        as: "idx",
                        in: {
                           $let: {
                              vars: {
                                 this: { $arrayElemAt: ["$x", "$$idx"] },
                                 prev: { $arrayElemAt: ["$x", { $subtract: ["$$idx", 1] }] }
                              },
                              in: {
                                 $cond: {
                                    if: { $and: [{ $eq: ["$$this", "$$prev"] }, { $gt: ["$$idx", 0] }] },
                                    then: null,
                                    else: "$$this"
                                 }
                              }
                           }
                        }
                     }
                  },
                  else: "$x"
               }
            }
         }
      }
      
      

      and

      {
         $set: {
            x: {
               $cond: {
                  if: { $isArray: "$x" },
                  then: {
                     $let: {
                        vars: {
                           values: {
                              $reduce: {
                                 input: "$x",
                                 initialValue: [],
                                 in: {
                                    $concatArrays: [
                                       "$$value",
                                       [{
                                          val: "$$this",
                                          new: {
                                             $cond: {
                                                if: { $ne: ["$$this", { $last: "$$value.val" }] },
                                                then: "$$this",
                                                else: null
                                             }
                                          }
                                       }]
                                    ]
                                 }
                              }
                           }
                        },
                        in: "$$values.new"
                     }
                  },
                  else: "$x"
               }
            }
         }
      }
      

      Both of them are working fine, however the firsts version with `$map: { input: { $range: ...` is around 10 times faster than the second version with `$reduce`.

      Is there a reason why `$reduce` is 10 times slower than `$map`? I would expect the second version little faster because array "$x" is read only once. Is there any possibility to improve it?

      My collection contains several million documents, the array size vary from 2 to 150k with an average of 50 elements.

      Kind Regards
      Wernfried

       

       

            Assignee:
            edwin.zhou@mongodb.com Edwin Zhou
            Reporter:
            wernfried.domscheit@sunrise.net Wernfried Domscheit
            Votes:
            0 Vote for this issue
            Watchers:
            11 Start watching this issue

              Created:
              Updated:
              Resolved: