使用 Mongoose 从 Decimal128 中提取十进制 - MongoDB

Posted

技术标签:

【中文标题】使用 Mongoose 从 Decimal128 中提取十进制 - MongoDB【英文标题】:Extract Decimal from Decimal128 with Mongoose - MongoDB 【发布时间】:2019-04-21 12:30:17 【问题描述】:

我正在使用 Mongoose 在 Nodejs 中查询 Mongo,并尝试提取存储为 Decimal128 的多个字段的数值。但是,该值奇怪地包含在查询结果中,我不确定如何通过 Mongo 或 Mongoose 提取它:

data:[
  
  "date": 
          "$numberDecimal": "1530057600000"
  ,
  "open": 
          "$numberDecimal": "86.13"
  ,
  "high": 
          "$numberDecimal": "86.63"
  ,
  "low": 
          "$numberDecimal": "85.47"
  ,
  "close": 
          "$numberDecimal": "85.64"
  ,
  "volume": 
          "$numberDecimal": "308508"
  
,

有没有办法可以使用 Mongo 或 Mongoose 将上面的 JSON 查询结果转换为下面的内容?

data:[
 
  "date": 1530057600000
  "open": 86.13
  "high": 86.63
  "low": 85.47
  "close": 85.64
  "volume": 308508
 ,

我尝试如下选择字段,但这不起作用。

    data[i].date.$numberDecimal, 
    data[i].open.$numberDecimal,
    data[i].high.$numberDecimal,
    data[i].low.$numberDecimal, 
    data[i].close.$numberDecimal 

这是我的 Mongoose 架构:

文件夹 - 模型 - Stock.js

const mongoose = require('mongoose')
mongoose.Promise = global.Promise

const childSchemaData = new mongoose.Schema(
  "_id": false,
  date: type: mongoose.Types.Decimal128,
  open: type: mongoose.Types.Decimal128,
  high: type: mongoose.Types.Decimal128,
  low: type: mongoose.Types.Decimal128,
  close: type: mongoose.Types.Decimal128,
  volume: type: mongoose.Types.Decimal128
)

const parentSchemaSymbol = new mongoose.Schema(
  "_id": false,
  symbol: 
    type: String,
    trim: true,
    minlength: 2,
    maxlength: 4,
    uppercase: true,
    required: 'Plese enter a valid symbol, min 2 characters and max 4'
  ,
  // Array of subdocuments
  data: [childSchemaData],
  slug: String

)

module.exports = mongoose.model('Stock', parentSchemaSymbol)

控制器

const mongoose = require('mongoose')
const parentSchemaSymbol = mongoose.model('Stock')

exports.dbFetch = (req, res) => 
  let curValueDbFetch = req.params.symbol

  const query =  symbol: `$curValueDbFetch` 
  const projection =  _id: 0, data: 1 

  parentSchemaSymbol.findOne(query, projection).then(doc => 
    return res.send(doc)
  ).catch(e => 
    console.log(e)
  )

我正在将数据发送到前端,这是我在浏览器中收到的:

解决方案

const mongoose = require('mongoose')
const parentSchemaSymbol = mongoose.model('Stock')

exports.dbFetch = (req, res) => 
  let curValueDbFetch = req.params.symbol

  const query =  symbol: `$curValueDbFetch` 
  const projection =  _id: 0, data: 1 

  parentSchemaSymbol.findOne(query, projection).sort( date: -1 ).then(doc => 
    let chartData = doc.data.map(item => 
      return 
        date: parseFloat(item.date), // the date
        open: parseFloat(item.open), // open
        high: parseFloat(item.high), // high
        low: parseFloat(item.low), // low
        close: parseFloat(item.close), // close
        volume: parseFloat(item.volume)// volume
      
    )
    res.send(chartData)
  )
    .catch(e => 
      console.log(e)
    )

【问题讨论】:

您不需要做任何事情。 javascript 对象上总是 toString(),这基本上是在字符串上下文中的任何访问中暗示的。您可以在任何值上使用parseFloat(),这实际上将再次暗示toString(),但当然Decimal128 的全部意义在于这些值对于float 来说太大且太精确。因此toString() 通常应该是这种方式。或者当然接受 $numberDecimal: "123.45" 的扩展 JSON 形式,因为它实际上是指示“类型”的“正确”实现。 @NeilLunn 您的意思是将 Schema 更改为 date: type: mongoose.Types.Decimal128 @NeilLunn 你能想出代码示例吗,因为没有任何变化 不确定你在说什么。你是在发送数据吗? res.send(doc) 实际上只是一个字符串化对象。 res.json(doc) 是数据的 JSON 字符串化形式。无论哪种速度,两者都应该调用toString()。如果您有不同的想法或仍然不明白我在说什么,也许可以显示一个带有当前输出和预期输出的MCVE。 @NeilLunn 我正在将数据发送到视图层以生成股票图表。 【参考方案1】:

工作解决方案

const mongoose = require('mongoose')
const parentSchemaSymbol = mongoose.model('Stock')

exports.dbFetch = (req, res) => 
  let curValueDbFetch = req.params.symbol

  const query =  symbol: `$curValueDbFetch` 
  const projection =  _id: 0, data: 1 

  parentSchemaSymbol.findOne(query, projection).sort( date: -1 ).then(doc => 
    let chartData = doc.data.map(item => 
      return 
        date: parseFloat(item.date), // the date
        open: parseFloat(item.open), // open
        high: parseFloat(item.high), // high
        low: parseFloat(item.low), // low
        close: parseFloat(item.close), // close
        volume: parseFloat(item.volume)// volume
      
    )
    res.send(chartData)
  )
    .catch(e => 
      console.log(e)
    )

【讨论】:

【参考方案2】:

方法一: .

使用 toString()。它将对象转换为字符串。

find((docs) => 
   let result = docs.map((doc) => 
       if(doc.open)
          doc.open = doc.open.toString();
       

       if(doc.close)
          doc.close = doc.close.toString();
       

       return doc;  
   );

    //send modified output
    res.json(result);
)

输出如下:-

/*
[
  
    "open":  "86.13",
    "close": "85.64"
  ,
]
*/

方法二: MongoDB 4.0 以上,

db.myCollection.aggregate([
  $match:
   //...
   //...
   ,


   $addFields : 
        open: "$toString" : "$open",
        close : "$toString" : "$close",
    ,
]);

【讨论】:

【参考方案3】:

这适用于任何领域!

它也支持子文档和子文档数组

const MySchema = new Schema(/*... schema fields ...*/);


const decimal2JSON = (v, i, prev) => 
  if (v !== null && typeof v === 'object') 
    if (v.constructor.name === 'Decimal128')
      prev[i] = v.toString();
    else
      Object.entries(v).forEach(([key, value]) => decimal2JSON(value, key, prev ? prev[i] : v));
  
;

MySchema.set('toJSON', 
  transform: (doc, ret) => 
    decimal2JSON(ret);
    return ret;
  
);

mongoose.model('MyModel', MySchema);

用法:

MyModel.findOne().then(data => console.log(data.toJSON());

【讨论】:

使用这个答案,尤其是当您在节点中定义和导出模式模型时。【参考方案4】:

使用lodash _.cloneDeepWith() 非常简单。遍历每个对象属性并将具有$numberDecimal 属性的对象转换为字符串。

// first flattenDecimals using mongoose `toJSON()`
var objectStep1= dbResult.toJSON(flattenDecimals: true);
//  use lodash _.cloneDeepWith() to iterate over every object property
var returnThisObject = _.cloneDeepWith(objectStep1, propVal =>
    if (_.has(propVal, '$numberDecimal')) return propVal.$numberDecimal;
);

或者,您可以在下面不使用toJSON() 来执行此操作,但我认为它的效率会降低,因为猫鼬结果有很多不属于结果的属性。然后你还需要检查未定义的属性。

var returnThisObject = _.cloneDeepWith(dbResult, propVal =>
    if (!propVal) return propVal; // check for undefined 
    if ('Decimal128' == propVal ._bsontype) return propVal.toString();
);

【讨论】:

以上是关于使用 Mongoose 从 Decimal128 中提取十进制 - MongoDB的主要内容,如果未能解决你的问题,请参考以下文章

解决mongodb中BigDecimal和Decimal128互转异常

如何使用 Python 生成随机的 Decimal128、Decimal256 数字

C#decimal是啥类型以及它的作用

mysqldecimal数据类型

C# float 、double和decimal的解释

float,double和decimal类型