2017-06-22 56 views
1

我有这样的样品测试集文件:浮点数不匹配的聚集

/* 1 */ 
{ 
    "_id" : 1.0, 
    "value" : 10.7 
} 

/* 2 */ 
{ 
    "_id" : 2.0, 
    "value" : 10.8 
} 

/* 3 */ 
{ 
    "_id" : 3.0, 
    "value" : 10.7 
} 

所以,当我使用$ addFields聚集管道使用查询的文档添加新的“结果”字段:

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 
} 

现在我想执行这个新添加的外商投资企业比较ld使用mongo查询:

db.test.aggregate([ 
    {$addFields:{result:{ $add : ["$value", .10]}}}, 
    { $match : { result : { $eq : 10.8}}} 
]); 

我看到的是上面的查询是正确的,但不知道为什么它返回没有文件匹配?

我在这里做错了什么吗?

回答

1

在你的问题中,你并没有真正说出全部真相。如果我参加了初始"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 } 

WEL来到计算机科学。这是浮点数学,它总是有舍入误差。对于完整的阅读,进入此:

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 } 
+0

感谢Neil您的回复,但我发布了正确的文档,其中包含测试集合及其mongo查询结果。您的解决方法确实奏效,但特别是在尝试使用不同的十进制数时构造mongo算术表达式并不有用。 –

+0

@SagarRathod是的,它工作。他们是浮点数,这就是你如何处理它们。按照我的例子**完全**。我正在使用您发布的相同数据。 –

+0

@SagarRathod我现在看到你的困惑。你在Robomongo(机器人3T)中运行这个应该已经证明了你如何在你的问题中呈现结果。它的错误,并没有正确显示浮点数。如果使用通用发行版中包含的plain ['mongo'](https://docs.mongodb.com/manual/reference/program/mongo/#bin.mongo)shell运行,则会看到与我一样的输出已发布。尽管如此,更正后的聚合语句即使在Robo 3T中也会选择正确的数据。 –

1

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 
}