使用 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互转异常