一個版本爲3.2的thinkphp項目打算使用mongodb進行接口的讀寫工做,因爲thinkphp自帶的mongodb driver版本太老,需本身開發個簡單的driver來管理各類方法。現把在開發中我的感受比較重要的信息點記錄下來。php
一、新版mongodb的鏈接方式,使用 \MongoDB\Driver\Manager類:mysql
1 $this->_conn = new \MongoDB\Driver\Manager($conf["url"] . "/{$conf["dbname"]}");
二、mongodb以文檔的形式儲存數據,也就是以多維數組形式儲存;新版的mongodb增刪改主要使用\MongoDB\Driver\BulkWrite類:sql
1 $bulk = new \MongoDB\Driver\BulkWrite;
新增操做(返回記錄id,與mysql的id是不同的):mongodb
1 $id = $bulk->insert($documents);
修改操做,第一個參數爲修改條件,第二個是修改的數值,第三個是額外選項(可選),例如若是$option = ['multi' => true]就會修改全部符合條件的數據,默認爲只修改匹配的第一條;thinkphp
1 $bulk->update($this->_where, $updates, $option);
刪除操做,第一個參數爲刪除條件,第二個是額外選項,例如若是設置['limit' => 1]則只刪除1條記錄:api
1 $bulk->delete($this->_where, $option);
添加了上述操做後,執行操做:數組
1 $writeConcern = new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY, 1000); 2 $result = $this->_conn->executeBulkWrite($this->_db . '.' . $this->_prefix . $this->_table, $bulk, $writeConcern); 3 4 /* If the WriteConcern could not be fulfilled */ 5 if ($writeConcernError = $result->getWriteConcernError()) { 6 throw new \Exception("%s (%d): %s\n", $writeConcernError->getMessage(), $writeConcernError->getCode(), var_export($writeConcernError->getInfo(), true)); 7 } 8 9 return true;
三、查詢。優化
mongodb可查詢到多重嵌套的數據,例如若是數據格式以下:this
1 [ 2 'supplier' => 'Skyworth', 3 'data' => [ 4 'id' => 2, 5 'height' => 150, 6 'weight' => [ 7 'morning' => 5, 8 'night' => [4,5,6], 9 ], 10 ] 11 ]
要查詢到'morning’字段的信息,可在where條件那裏填寫['data.weight.morning' => 5]來進行篩選,普通的查詢方法使用\MongoDB\Driver\Query類進行查詢:url
1 $query = new \MongoDB\Driver\Query($this->_where, $writeOps); 2 $readPreference = new \MongoDB\Driver\ReadPreference(\MongoDB\Driver\ReadPreference::RP_PRIMARY); 3 $cursor = $this->_conn->executeQuery($this->_db . "." . $this->_prefix . $this->_table, $query, $readPreference); 4 var_dump($cursor->toArray());
\MongoDB\Driver\Query有兩個參數,除了第一個的where以外,另外的$writeOps可限定字段,例如在php下可這樣寫:
$writeOps = [ 'projection' => [ '字段1' => 1, '字段2' => 1, '_id' => 0, ] ]
當填寫了‘projection’後,mongodb就只會輸出你想輸出的字段,其中1表明要輸出,但'_id'默認是會輸出的,若是不想讓'_id'輸出,只須要填寫'_id'爲0便可;
四、聚合(aggregate)操做
聚合操做相似於mysql把數據整合,得到平均值,最大值等操做;主要使用\MongoDB\Driver\Command類:
1 $command = new \MongoDB\Driver\Command($this->_where); 2 $cursor = $this->_conn->executeCommand($this->_db ,$command); 3 var_dump($cursor->toArray());
在查詢方法的寫法上費了老大的勁,目前只瞭解一種寫法:
1 [ 2 'aggregate' => 'api_record', 3 'pipeline' => [ 4 [ 5 '$match' => ['supplier' => 'JD'], 6 '$group' => ['_id' => '$supplier', 'sum' => ['$sum' => '$data.height']] 7 ], 8 ], 9 'cursor' => new \stdClass, 10 ]
aggregate爲要操做的集合(mysql的表),pipeline爲須要操做的集合(注意這裏的數組裏邊還得加上一個數組)。要操做的表達式前面有一個$的符號,上面的操做是先$match匹配supplier字段爲JD的數據,而後經過$group分組,得到他們數據裏邊,data字段裏邊的height字段的數據總和(這裏確定是只得到一條數據。。)。而cursor的意思是把查詢出來的數據格式優化成能看懂的格式。
暫時記錄到這裏,在後面開發再繼續補充。
補:aggregate無所不能!!!
五、Map Reduce
教程中描述在用MongoDB查詢返回的數據量很大的狀況下,作一些比較複雜的統計和聚合操做作花費的時間很長的時候,能夠用MongoDB中的MapReduce進行實現,但我的感受reduce能寫的方法沒有aggregate提供的表達式多,並且在功能上有點重疊,可能須要進一步的研究。現提供php下的實現方法:
$map = <<<JS function test(){ emit({ 'saleUnit':this.saleUnit, 'upc':this.upc }, 2); } JS; $map = new \MongoDB\BSON\Javascript($map); $reduce = <<<JS function test(key, values){ var total = 0; for(var i in values){ total = parseInt(total) + parseInt(i); } return total; } JS; $reduce = new \MongoDB\BSON\Javascript($reduce); $out = 'output_collection_name'; $data = Mon('member_store_record')->where([ 'mapReduce' => 'suning_product', 'map' => $map, 'reduce' => $reduce, 'out' => 'output_collection_name', ])->command(false); var_dump($data);
mapReduce指使用哪一個集合來處理,out指輸出結果放到哪一個集合,map和reduce方法都是使用js來寫的,而後放到\MongoDB\BSON\Javascript類處理。返回結果示例以下:
array (size=4) 'result' => string 'output_collection_name' (length=22) 'timeMillis' => int 106 'counts' => array (size=4) 'input' => int 4 'emit' => int 4 'reduce' => int 1 'output' => int 2 'ok' => float 1
注意上面的是map_reduce的處理狀況分析,處理的結果應該是在output_collection_name的集合查看。上面結果timeMillis指運行了多少毫秒,input指輸入多少條數據,而後最終output輸出了多少條數據到output_collection_name集合;