社區內一哥們@smcboy 提出關於php中操做MongoDB存儲整數問題,找到點資料花點時間翻譯過來,是個很好的學習方式。@紅薯 那篇討論個人修改回覆,仍然沒有更新可惡啊~!!說實話我就是高一英語水平 爲了這篇文章我算是絞盡腦汁,翻譯了大半天,累死我了。科學精神難得、難得!! php
在我當前項目中大量是MongoDB,正在從傳統RDBMS過分到key-value存儲。Facebook中用戶標識UserID使用64位Int數據類型存儲,杯具的是 MongoDB的PHP驅動只支持32位整型數據,致使UserID被截斷沒法處理Facebook用戶信息。 html
MongoDB數據採用BSON(Binary JSON)文檔型存儲,BSON有兩種整型數據類型,一、32位有符號整型數據(INT); 二、64位有符號型整型數據(LONG)。因爲PHP不支持大於8個字節整數,因此MongoDB PHP驅動只支持32位有符號整型數據存儲。然而這樣不是絕對的,在C類型 long 爲64位平臺上,PHP仍然能夠正常支持64位整型數據; 除了在Windowns上,其餘平臺上C中long類型老是32位。
當PHP中整型存儲到MongoDB中,PHP驅動會採用最低兼容原則用32位進行轉換存儲到MongoDB文檔中。下面是測試案例(測試平臺爲 64位): git
$m = new Mongo(); $c = $m->selectCollection('test', 'inttest'); $c->remove(array()); //插入大於32位數據 $c->insert(array('number' => 1234567890123456)); $r = $c->findOne(); echo $r['number'], "\n";
輸出: github
int(1015724736)二進制解析:
1234567890123456 = 100011000101101010100111100100010101011101011000000 1015724736 = 111100100010101011101011000000
代碼:
mongodb
ini_set('mongo.native_long', 1); $c->insert(array('number' => 1234567890123456)); $r = $c->findOne(); var_dump($r['number']);輸出:
int(1234567890123456)
在64位平臺中,PHP程序中配置mongo.native_long 容許使用完整64位整型存儲到MongoDB,本例中這種方式存儲到MongoDB中類型爲BSON LONG, 若是未開啓此配置則類型爲BSON INT類型。該配置對從MongoDB讀取數據到PHP中一樣有效。若是關閉該配置,當從MongoDB取出數據時PHP驅動會把 BSON LONG 類型轉換爲PHP的double類型,形成精度損失。下面看個例子: shell
ini_set('mongo.native_long', 1); //開啓配置 $c->insert(array('number' => 12345678901234567)); ini_set('mongo.native_long', 0); //關閉配置 $r = $c->findOne(); var_dump($r['number']);輸出:
float(1.2345678901235E+16)在32位平臺中 mongo.native_log 參數配置不起任何做用,仍然會以BSON INT 類型存儲。
$int32 = new MongoInt32("32091231"); $int64 = new MongoInt64("1234567980123456");使用該對象能夠像正常使用插入、更新、查詢等操做
$m = new Mongo(); $c = $m->selectCollection('test', 'inttest'); $c->remove(array()); $c->insert(array( 'int32' => new MongoInt32("1234567890"), 'int64' => new MongoInt64("12345678901234567"), )); $r = $c->findOne(); var_dump($r['int32']); var_dump($r['int64']);輸出結果:
int(1234567890) float(1.2345678901235E+16)能夠看到對返回結果沒任何改變。BSON INT類型仍然是 int型,BSON LONG 類型變爲 double類型。若是我啓用 mongo.native_long 配置,經過MongoInt64類庫轉換,在64位平臺上,PHP中獲取 BSON LONG 會返回正確int型,在32位平臺上MongoCursorException會拋出提示信息。
$m = new Mongo(); $c = $m->selectCollection('test', 'inttest'); $c->remove(array()); $c->insert(array( 'int64' => new MongoInt64("12345678901234567"), )); ini_set('mongo.long_as_object', 1); $r = $c->findOne(); var_dump($r['int64']); echo $r['int64'], "\n"; echo $r['int64']->value, "\n";輸出:
object(MongoInt64)#7 (1) { ["value"]=> string(17) "12345678901234567" } 12345678901234567 12345678901234567MongoInt32和MongoInt64 類基於對象的__toString()實現,因此返回的value值能夠直接進行 echo,你只能獲取一個整型字符串,因此請意識到MongoDB是類型敏感的,不會用對待字符串的方式對待數字,數字就是數字。
ini_set('mongo.native_long', 1); $m = new Mongo(); $c = $m->selectCollection('test', 'inttest'); $c->remove(array()); $nr = "12345678901234567"; $c->insert(array('int64' => new MongoInt64($nr))); $r = $c->findOne(array('int64' => $nr)); // $nr is a string here var_dump($r['int64']); $r = $c->findOne(array('int64' => (int) $nr)); var_dump($r['int64']);輸出:
NULL int(12345678901234567)
下面列出關於不一樣的參數啓用狀態,整型轉換狀況: json
PHP to MongoDB (32位系統) From PHP 函數 |
Stored in Mongo 學習 |
|
native_long=0 測試 |
native_long=1 |
|
1234567 |
INT(1234567) |
INT(1234567) |
123456789012 |
FLOAT(123456789012) |
FLOAT(123456789012) |
MongoInt32("1234567") |
INT(1234567) |
INT(1234567) |
MongoInt64("123456789012") |
LONG(123456789012) |
LONG(123456789012) |
PHP to MongoDB (64位系統):
From PHP |
Stored in Mongo |
|
native_long=0 |
native_long=1 |
|
1234567 |
INT(1234567) |
LONG(1234567) |
123456789012 |
garbage |
LONG(123456789012) |
MongoInt32("1234567") |
INT(1234567) |
INT(1234567) |
MongoInt64("123456789012") |
LONG(123456789012) |
LONG(123456789012) |
Stored in Mongo |
Returned to PHP as |
||
long_as_object=0 |
long_as_object=1 |
||
native_long=0 |
native_long=1 |
||
INT(1234567) |
int(1234567) |
int(1234567) |
int(1234567) |
LONG(123456789012) |
float(123456789012) |
MongoCursorException |
MongoInt64("123456789012") |
Mongo to PHP (64位系統):
Stored in Mongo |
Returned to PHP as |
||
long_as_object=0 |
long_as_object=1 |
||
native_long=0 |
native_long=1 |
||
INT(1234567) |
int(1234567) |
int(1234567) |
int(1234567) |
LONG(123456789012) |
float(123456789012) |
int(123456789012) |
MongoInt64("123456789012") |
總結:
綜上所述能夠看到想得到64位的支持仍是很棘手的,若是你只須要在64爲平臺上運行代碼,咱們推薦使用 mongo.native_long=1 配置參數。當整數存儲到MongoDB,取出是仍然是整型數據,從而達到支持64位的目的。
若是你丫就是想要在32位平臺(包含Windows 64位上的PHP),你沒辦法使用獲得可靠的整型數據,必須使用MongoInt64 類來實現。這也會帶來其餘問題,如:你必須在初始化的時候處理字符串類型的數字。也要注意MongoDB Shell 將全部的數字做爲float浮點型數據處理,這並不能表明64位整型數字,相反將做爲浮點型數字。全部不要在shell模式下進行數據修改,這樣會致使類型轉換!!
案例:
$m = new Mongo(); $c = $m->selectCollection('test', 'inttest'); $c->remove(array()); $c->insert(array('int64' => new MongoInt64("123456789012345678")));MongoDB Shell模式下:
$ mongo MongoDB shell version: 1.4.4 url: test connecting to: test type "help" for help > use test switched to db test > db.inttest.find() { "_id" : ObjectId("4c5ea6d59a14ce1319000000"), "int64" : { "floatApprox" : 123456789012345680, "top" : 28744523, "bottom" : 2788225870 } }當咱們經過驅動獲取支持64位數據,能夠獲得靠譜的結果:
ini_set('mongo.long_as_object', 1); $r = $c->findOne(); var_dump($r['int64']);輸出:
object(MongoInt64)#7 (1) { ["value"]=> string(18) "123456789012345678" }
這個新函數方式將會在 mongo 1.0.9 release 版本中推出,能夠經過PRCL pecl install mongo 獲取。
剩下的就靠命運了,祝你好運。
翻譯:OSC民工