我有这个示例测试集合文档:
/* 1 */
{
"_id" : 1.0,
"value" : 10.7
}
/* 2 */
{
"_id" : 2.0,
"value" : 10.8
}
/* 3 */
{
"_id" : 3.0,
"value" : 10.7
}
所以当我在聚合管道中使用$ addFields时,使用查询在文档中添加新的“result”字段:
db.test.aggregate([{$addFields:{result:{ $add : ["$value", .10]}}}]);
它给出了以下结果:
/* 1 */
{
"_id" : 1.0,
"value" : 10.7,
"result" : 10.8
}
/* 2 */
{
"_id" : 2.0,
"value" : 10.8,
"result" : 10.9
}
/* 3 */
{
"_id" : 3.0,
"value" : 10.7,
"result" : 10.8
}
现在我想使用mongo查询与这个新添加的字段进行比较:
db.test.aggregate([
{$addFields:{result:{ $add : ["$value", .10]}}},
{ $match : { result : { $eq : 10.8}}}
]);
我看到的是上面的查询是正确的,但不确定为什么它没有返回匹配的文件?
我在这里做错了吗?
答案 0 :(得分:2)
在你的问题中,你并没有真正说出全部真相。如果我采用最初的"value"
字段并将其添加到0.1
,那么结果就是我期望的结果:
所以插入文件:
db.numbers.insert([
{ "_id" : 1.0, "value" : 10.7 },
{ "_id" : 2.0, "value" : 10.8 },
{ "_id" : 3.0, "value" : 10.7 }
])
然后运行相同的初始聚合语句:
db.numbers.aggregate([
{ "$addFields":{
"result": { "$add": [ "$value", 0.10 ] }
}}
]);
结果:
{ "_id" : 1, "value" : 10.7, "result" : 10.799999999999999 }
{ "_id" : 2, "value" : 10.8, "result" : 10.9 }
{ "_id" : 3, "value" : 10.7, "result" : 10.799999999999999 }
欢迎来到计算机科学。这是浮点数学,它总是有舍入误差。如需完整阅读,请进入:
What Every Computer Scientist Should Know About Floating-Point Arithmetic
我们可以简单地通过不使用小数和舍入到因式整数来纠正这个问题。在这种情况下 x10 :
db.numbers.aggregate([
{ "$addFields": {
"result": {
"$divide": [
{ "$add": [
{ "$multiply": [ "$value", 10 ] },
1
]},
10
]
}
}}
])
"result"
出现的地方是这样的:
{ "_id" : 1, "value" : 10.7, "result" : 10.8 }
{ "_id" : 2, "value" : 10.8, "result" : 10.9 }
{ "_id" : 3, "value" : 10.7, "result" : 10.8 }
最终$match
仅限于要求的值:
db.numbers.aggregate([
{ "$addFields": {
"result": {
"$divide": [
{ "$add": [
{ "$multiply": [ "$value", 10 ] },
1
]},
10
]
}
}},
{ "$match": { "result": 10.8 } }
])
正确的结果
{ "_id" : 1, "value" : 10.7, "result" : 10.8 }
{ "_id" : 3, "value" : 10.7, "result" : 10.8 }
答案 1 :(得分:2)
JavaScript中的默认(当前也是唯一的)本机数字类型是双精度浮点Number。正如在关于这个问题的其他讨论中所指出的,二进制浮点算法受到舍入误差的影响,因为一些小数部分cannot be represented exactly in binary floating point。一个常见的解决方法是使用数据模型方法using a Scale Factor来避免存储小数值。
但是,如果您使用的是MongoDB 3.4+,那么在使用浮点或货币值时,您可以使用本机Decimal BSON type来提高精度。这实现了IEEE 754 Decimal 128 floating-point format,它支持精确的十进制表示,包括通过MongoDB的聚合管道进行算术操作。
mongo
shell包含一个NumberDecimal
帮助程序,用于将十进制值传递给聚合查询或CRUD命令。
设置一些示例数据:
db.test.insert([
{ "_id" : 1.0, "value" : NumberDecimal(10.7) },
{ "_id" : 2.0, "value" : NumberDecimal(10.8) }
])
...并使用聚合来比较原始值和递增结果:
db.test.aggregate([
{ $addFields: {
"result" : { "$add": [ "$value", NumberDecimal(0.10) ] },
}},
// Compare value and result against expected value of 10.8
{ $addFields: {
"matches_value": { $eq: ["$value", NumberDecimal(10.8)] },
"matches_result": { $eq: ["$result", NumberDecimal(10.8)] }
}},
])
十进制类型正确保持精度,可用于完全匹配。此聚合的示例输出:
{
"result": [
{
"_id": 1,
"value": NumberDecimal("10.7000000000000"),
"result": NumberDecimal("10.800000000000000"),
"matches_value": false,
"matches_result": true
},
{
"_id": 2,
"value": NumberDecimal("10.8000000000000"),
"result": NumberDecimal("10.900000000000000"),
"matches_value": true,
"matches_result": false
}
],
"ok": 1
}