Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extract Decimal from NumberDecimal with Mongo(ose)

In the Numeric Model for storing monetary values, the MongoDB docs state:

From the mongo shell decimal values are assigned and queried using the NumberDecimal() constructor.

Similarly, when using the Morphia Java library, BigDecimals are automatically inserted as BigDecimals.

I'm querying Mongo in Node with Mongoose and attempting to extract the numeric value of a field stored as a NumberDecimal. However, the value is oddly wrapped in query results and I'm not sure how to extract it through Mongo or Mongoose:

[  
   {  
      "openValue":{  
         "$numberDecimal":"119.931"
      },
      "timestamp":"2017-01-20T10:30:00.000Z"
   },
   {  
      "openValue":{  
         "$numberDecimal":"119.965"
      },
      "timestamp":"2017-01-20T10:31:00.000Z"
   }
]

One post I read stated using parseFloat() in my application code will perform what I desire, however it's not efficient to iterate through the result to perform this transformation. Avoiding iterating and transforming would mean running the function on the NumberDecimals whenever I want their value every time, which would be annoying.

Is there a way I can use Mongo or Mongoose to convert the above JSON query-result into what's below?

[  
   {  
      "openValue": 119.931,
      "timestamp":"2017-01-20T10:30:00.000Z"
   },
   {  
      "openValue": 119.965,
      "timestamp":"2017-01-20T10:31:00.000Z"
   },
   {  
      "openValue": 119.975,
      "timestamp":"2017-01-20T10:32:00.000Z"
   }
]

I tried selecting the field as ...openValue.$numberDecimal, but this didn't work. Thank you!

Edit: Here's my Mongoose schema:

var EquityHistoryModel = new Schema({
   _id: {
      equityIdentifier: { type: {
         exchange: { type: String, index: true },
         symbol: { type: String, index: true }
      }, index: true },
      instant: { type: Date, index: true },
      durationMinutes: { type: Number }
   },
   open: { type: mongoose.Schema.Types.Decimal },
   high: { type: mongoose.Schema.Types.Decimal },
   low: { type: mongoose.Schema.Types.Decimal },
   close: { type: mongoose.Schema.Types.Decimal },
   volume: { type: Number },
   isDividendAdjusted: { type: Boolean },
   isSplitAdjusted: { type: Boolean }
}, { collection: 'equityHistories', versionKey: false });

Here's the Mongoose query for the first JSON result above:

mongo.EquityHistoryModel.aggregate([  
   {  
      "$match":{  
         "_id.equityIdentifier.exchange":passed_in,
         "_id.equityIdentifier.symbol":passed_in,
         "_id.instant":passed_in
      }
   },
   {  
      "$project":{  
         "_id":0,
         "openValue":"$open",
         "timestamp":"$_id.instant"
      }
   }
],
like image 541
Matt Goodrich Avatar asked Jan 30 '17 10:01

Matt Goodrich


2 Answers

You can also overwrite the toJSON method:

// Never return '__v' field and return the 'price' as String in the JSON representation
// Note that this doesn't effect `toObject`
EquityHistoryModel.set('toJSON', {
  getters: true,
  transform: (doc, ret) => {
    if (ret.price) {
      ret.price = ret.price.toString();
    }
    delete ret.__v;
    return ret;
  },
});
like image 68
Gianfranco P. Avatar answered Nov 03 '22 13:11

Gianfranco P.


Contrary to what I expected, it seems the values are automatically extracted. Stringifying the result automatically wraps the value in the NumberDecimal. See the code with the output manually placed below:

console.log(docs[0].openValue);
119.800
console.log(JSON.stringify(docs[0].openValue]);
{"$numberDecimal":"119.800"}

Also, I struggled with sending query results, due to res.json or res.send using stringify. Instead I wrote a replace function and set the property in Node.

like image 45
Matt Goodrich Avatar answered Nov 03 '22 15:11

Matt Goodrich