Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongodb calculation query--cummulative multiplication

I recently started working in Mongodb for POC. I have one json collection below

db.ccpsample.insertMany([
  {
    "ccp_id":1,
    "period":601,
    "sales":100.00
  },
  {
    "ccp_id":1,
    "period":602,
    "growth":2.0,
    "sales":"NULL"    ##sales=100.00*(1+(2.0/100)) -- 100.00 comes from(ccp_id:1 and period=601) 
  },
  {
    "ccp_id":1,
    "period":603,
    "growth":3.0,
    "sales":"NULL"   ##sales=100.00*(1+(2.0/100))**(1+(3.0/100))-- 100.00 comes from(ccp_id:1 and period=601) 2.0 comes from (ccp_id:2 and period=602)  
  },
  {
    "ccp_id":2,
    "period":601,
    "sales":200.00
  },
  {
    "ccp_id":2,
    "period":602,
    "growth":2.0,
    "sales":"NULL"   ##sales=200.00*(1+(2.0/100))
  },
  {
    "ccp_id":2,
    "period":603,
    "growth":3.0,
    "sales":"NULL"   ##same like above
  }
])

And i need to calculate sales field which has NULL by using above documents with matching conditions of ccp_id should same and period field should be equal to 601. I have added a line to demonstrate calculation of sales field in collection itself above. I tried with $graphlookup but no luck. Can you people kindly help or suggest some way?

like image 516
Tharunkumar Reddy Avatar asked Mar 22 '18 14:03

Tharunkumar Reddy


People also ask

How do you multiply values in MongoDB?

MongoDB provides different types of arithmetic expression operators that are used in the aggregation pipeline stages and $multiply operator is one of them. This operator is used to multiply one number to another number and returns the result.

How do I use distinct aggregation in MongoDB?

You can use $addToSet with the aggregation framework to count distinct objects. Not a generic solution, if you have a large number of unique zip codes per result, this array would be very large. The question was to get the city with MOST zip codes for each state, not to get the actual zip codes.


1 Answers

You can use below aggregation:

db.ccpsample.aggregate([
  { $sort: { ccp_id: 1, period: 1 } },
  { 
    $group: {
      _id: "$ccp_id",
      items: { $push: "$$ROOT" },
      baseSale: { $first: "$sales" },
      growths: { $push: "$growth" }
    }
  },
  {
    $unwind: {
      path: "$items",
      includeArrayIndex: "index"
    }
  },
  {
    $project: {
      cpp_id: "$items.cpp_id",
      period: "$items.period",
      growth: "$items.growth",
      sales: {
        $cond: {
          if: { $ne: [ "$items.sales", "NULL" ] },
          then: "$items.sales",
          else: {
            $reduce: {
              input: { $slice: [ "$growths", "$index" ] },
              initialValue: "$baseSale",
              in: { $multiply: [ "$$value", { $add: [1, { $divide: [ "$$this", 100 ] }] } ] }
            }
          }
        }
      }
    }
  }
])

Basically to calculate the value for n-th element you have to know following things:

  • sales value of first element ($first in $group)
  • the array of all growths ($push in $group)
  • the n which indicates how many multiplications you have to perform

To calculate the index you should $push all elements into one array and then use $unwind with includeArrayIndex option which will insert the index of unwinded array to field index.

Last step calculates the cumulative multiplication. It uses $slice with index field to evaluate how many growths should be processed. So there will be one element for 601, two elements for 602 and so on.

Then it's time for $reduce to process that array and perform the multiplications based on your formula: (1 + (growth/100))

like image 127
mickl Avatar answered Oct 29 '22 12:10

mickl