2017-06-22 58 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 
}