澳门新葡萄京官网首页 1

PHP操作MongoDB时的整数问题及对策说明

MongoDB自己有两种整数类型,分别是:叁十位整数和63人整数,但旧版的PHP驱动不管操作系统是叁十几位依然61个人,把具有整数都看作叁拾人整数管理,结果形成陆十个人整数被截断。为了在尽量保持宽容性的前提下消亡那些标题,新版PHP驱动参预了mongo.native-long接纳,以期在六15人操作系统中把整数都看作六16位来拍卖,有野趣的可参看:64-bit integers in
MongoDB。

MongoDB纵然不像大家常用的mysql,sqlserver,oracle等关系型数据库有group
by函数那样方便分组,不过MongoDB要兑现分组也许有3个主意:

那么PHP驱动真的完全减轻了整数难点么?NO!在管理group操作的时候还会有BUG:

 * Mongodb三种分组织承办法:

为了印证难题,我们先来生成一些测量检验数据:

 * 1、group(先挑选再分组,不扶助分片,对数据量有所节制,作用不高卡塔尔

<?php
ini_set(‘mongo.native_long’, 1);
$instance = new Mongo();
$instance = $instance->selectCollection(‘test’, ‘test’);
for ($i = 0; $i < 10; $i++) {
$instance->insert(array(
‘group_id’ => rand(1, 5),
‘count’ => rand(1, 5),
));
}
?>

 * 2、mapreduce(基于js引擎,单线程实践,效能异常低,相符用做后台总结等卡塔尔国

下边让大家选择group操作,依据group_id分组,汇总总计count:

 * 3、aggregate(推荐卡塔尔(假诺您的PHP的mongodb驱动版本需>=1.3.0,推荐您使用aggregate,品质要高超多,而且动用上要轻便些,不过1.3的一时还不援助账户评释方式,能够透过 

<?php
ini_set(‘mongo.native_long’, 1);
$instance = new Mongo();
$instance = $instance->selectCollection(‘test’, ‘test’);
$keys = array(‘group_id’ => 1);
$initial = array(‘count’ => 0);
$reduce = ‘
function(obj, prev) {
prev.count += obj.count;
}
‘;
$result = $instance->group($keys, $initial, $reduce);
var_dump($result);
?>

上面就来看下mapreduce格局:

结果和预期的有出入,count未有达成拉长,而是成为了[object
Object],近些日子,若是必需接纳group操作,那么有二种办法能够消除这些主题素材:

 

ini_set(‘mongo.native_long’, 0);
$initial = array(‘count’ => (float)0);

Mongodb官网对MapReduce介绍:

那二种格局都是治标不治本的权宜之计,既然当前PHP驱动里group的达成慰难题,那我们就绕开它,用其它的办法完毕平等的功用,那个点子正是MapReduce:

Map/reduce in MongoDB is useful for batch processing of data and
aggregation operations. It is similar in spirit to using something like
Hadoop with all input coming from a collection and output going to a
collection. Often, in a situation where you would have used GROUP BY in
SQL, map/reduce is the right tool in MongoDB.

<?php
ini_set(‘mongo.native_long’, 1);
$instance = new Mongo();
$instance = $instance->selectDB(‘test’);
$map = ‘
function() {
emit(this.group_id, this.count);
}
‘;
$reduce = ‘
function(key, values) {
var sum = 0;
for (var index in values) {
sum += values[index];
}
return sum;
}
‘;
$result = $instance->command(array(
‘mapreduce’ => ‘test’,
‘map’ => $map,
‘reduce’ => $reduce
));
$result =
iterator_to_array($instance->{$result[‘result’]}->find());
var_dump($result);
?>

大概意思是:Mongodb中的Map/reduce首尽管用来对数码举行批量管理和集纳操作,有一点相似于采纳Hadoop对聚焦数据实行管理,全体输入数据都是从集结中得到,而MapReduce后输出的数码也都会写入到集合中。平常近似于大家在SQL中应用Group
By语句同样。
接纳MapReduce要兑现八个函数:Map和Reduce。Map函数调用emit(key,value卡塔尔(قطر‎遍历会集中负有的记录,将key与value传给Reduce函数进行管理。Map函数和Reduce函数是使用Javascript编写的,并能够通过db.runCommand或mapreduce命令来施行MapReduce操作。

把大象放三门冰箱里要求三步,而接纳MapReduce仅仅供给Map和Reduce两步就可以,这里有一个PDF文书档案生动的辨证了MySQL中GROUP
BY和MongoDB中MapReduce的照拂关系:

 

澳门新葡萄京官网首页 1 

MapReduce命令如下:
[javascript]
db.runCommand( 
{ mapreduce : <collection>, 
   map : <mapfunction>, 
   reduce : <reducefunction> 
   [, query : <query filter object>] 
   [, sort : <sort the query.  useful for optimization>] 
   [, limit : <number of objects to return from collection>] 
   [, out : <output-collection name>] 
   [, keeptemp: <true|false>] 
   [, finalize : <finalizefunction>] 
   [, scope : <object where fields go into javascript global scope
>] 
   [, verbose : true] 
 } 
); 

SQL to
MongoDB

db.runCommand(
{ mapreduce : <collection>,
澳门新葡萄京官网首页 ,   map : <mapfunction>,
   reduce : <reducefunction>
   [, query : <query filter object>]
   [, sort : <sort the query.  useful for optimization>]
   [, limit : <number of objects to return from collection>]
   [, out : <output-collection name>]
   [, keeptemp: <true|false>]
   [, finalize : <finalizefunction>]
   [, scope : <object where fields go into javascript global scope
>]
   [, verbose : true]
 }
);

此外,还应该有众多素材可供参照他事他说加以调查,如:MongoDB
Aggregation III: Map-Reduce
Basics。

参数表达:

证实:软件版本为MongoDB(1.6.5),PECL
Mongo(1.1.4)。不一样版本结论也许分化。

mapreduce:要操作的指标集结

map:映射函数(生成键值对队列,作为Reduce函数的参数)

reduce:总结函数

query:目的志录过滤

sort:对目的识录排序

limit:节制目的识录数据

out:总括结果寄放会集(即使不钦定则利用有的时候集结,在客商端断开后自行删除)

keeptemp:是不是保留有的时候集结

finalize:最后管理函数(对reduce重返结果实践最终收拾后存入结果集合)

scope:向map、reduce、finalize导入外界变量

verbose:突显详细的岁月总计音信

 

map函数
map函数调用当前指标,并处里对象的属性,传值给reduce,map方法应用this来操作当前指标,起码调用一遍emit(key,value卡塔尔国方法来向reduce提供参数,当中emit的key为末段数额的id。

 

reduce函数
接收一个值和数组,依照需求对数组进行统一分组等管理,reduce的key便是emit(key,value卡塔尔的key,value_array是同个key对应的八个value数组。

 

Finalize函数
此函数为可选函数,可在实施完map和reduce后施行,对终极的数据开展合併管理。

 

看完基本介绍,大家再来看叁个实例:

已知集结feed,测验数据如下:
[javascript]

   “_id”: ObjectId(“50ccb3f91e937e2927000004”), 
   “feed_type”: 1, 
   “to_user”: 234, 
   “time_line”: “2012-12-16 01:26:00” 

 

   “_id”: ObjectId(“50ccb3ef1e937e0727000004”), 
   “feed_type”: 8, 
   “to_user”: 123, 
   “time_line”: “2012-12-16 01:26:00” 

 

   “_id”: ObjectId(“50ccb3e31e937e0a27000003”), 
   “feed_type”: 1, 
   “to_user”: 123, 
   “time_line”: “2012-12-16 01:26:00” 

 

   “_id”: ObjectId(“50ccb3d31e937e0927000001”), 
   “feed_type”: 1, 
   “to_user”: 123, 
   “time_line”: “2012-12-16 01:26:00” 

{
   “_id”: ObjectId(“50ccb3f91e937e2927000004”),
   “feed_type”: 1,
   “to_user”: 234,
   “time_line”: “2012-12-16 01:26:00”
}

{
   “_id”: ObjectId(“50ccb3ef1e937e0727000004”),
   “feed_type”: 8,
   “to_user”: 123,
   “time_line”: “2012-12-16 01:26:00”
}

{
   “_id”: ObjectId(“50ccb3e31e937e0a27000003”),
   “feed_type”: 1,
   “to_user”: 123,
   “time_line”: “2012-12-16 01:26:00”
}

{
   “_id”: ObjectId(“50ccb3d31e937e0927000001”),
   “feed_type”: 1,
   “to_user”: 123,
   “time_line”: “2012-12-16 01:26:00”
}

 

大家按动态类型feed_type和用户to_user举办分组总结,达成结果:
feed_type to_user cout
1 234 1
8 123 1
1 123 2

 

 

 

 

 

 

 

落实代码:

[php]
/编写map函数  
$map = ‘ 
     function() { 
      var key = {to_user:this.to_user,feed_type:this.feed_type}; 
      var value = {count:1}; 
      emit(key,value); 
    } ‘;  
 
//reduce 函数  
$reduce = ‘ 
     function(key, values) { 
         var ret = {count:0}; 
     for(var i in values) { 
          ret.count += 1; 
      } 
      return ret; 
      }’; 
 
//查询条件  
$query = null;  //本实例中平昔不查询条件,设置为null 

//编写map函数
$map = ‘
     function() {
   var key = {to_user:this.to_user,feed_type:this.feed_type};
   var value = {count:1};
   emit(key,value);
    } ‘;

//reduce 函数
$reduce = ‘
     function(key, values) {
         var ret = {count:0};
  for(var i in values) {
       ret.count += 1;
   }
   return ret;
      }’;

//查询条件
$query = null;  //本实例中尚无查询条件,设置为null[php] view
plaincopyprint?$mongo = new
Mongo(‘mongodb://root:[email protected]:
28017/’State of Qatar; //链接mongodb,账号和密码为root,root 
$instance = $mongo->selectDB(“testdb”); 
 
//实施此命令后,会创立feed_temp_res的有时集合,并将总括后的数目放在该集结中  
$cmd = $instance->command(array( 
        ‘mapreduce’ => ‘feed’, 
        ‘map’       => $map, 
        ‘reduce’    => $reduce, 
        ‘query’ => $query, 
        ‘out’ => ‘feed_temp_res’ 
)); 
 
//查询一时集结中的总计数据,验证总计结果是或不是和预期结果意气风发致  
$cursor =
$instance->selectCollection(‘feed_temp_res’)->find(); 
$result = array(); 
try { 
    while ($cursor->hasNext()) 
    { 
        $result[] = $cursor->getNext(); 
    } 

catch (MongoConnectionException $e) 

    echo $e->getMessage(); 

catch (MongoCursorTimeoutException $e) 

    echo $e->getMessage(); 

catch(Exception $e){ 
    echo $e->getMessage(); 

 
//test  
var_dump($result); 

$mongo = new
Mongo(‘mongodb://root:[email protected]:
28017/’卡塔尔(قطر‎; //链接mongodb,账号和密码为root,root
$instance = $mongo->selectDB(“testdb”);

//实施此命令后,会创建feed_temp_res的这段日子集结,并将总结后的数码放在该集结中
$cmd = $instance->command(array(
  ‘mapreduce’ => ‘feed’,
  ‘map’       => $map,
  ‘reduce’    => $reduce,
  ‘query’ => $query,
  ‘out’ => ‘feed_temp_res’
));

//查询有时群集中的总结数据,验证总括结果是不是和预期结果意气风发致
$cursor =
$instance->selectCollection(‘feed_temp_res’)->find();
$result = array();
try {
 while ($cursor->hasNext())
 {
  $result[] = $cursor->getNext();
 }
}
catch (MongoConnectionException $e)
{
 echo $e->getMessage();
}
catch (MongoCursorTimeoutException $e)
{
 echo $e->getMessage();
}
catch(Exception $e){
 echo $e->getMessage();
}

//test
var_dump($result);
上边是出口的结果,和预期结果同样

[javascript]

   “_id”: { 
     “to_user”: 234, 
     “feed_type”: 1  
  }, 
   “value”: { 
     “count”: 1  
  }  

 

   “_id”: { 
     “to_user”: 123, 
     “feed_type”: 8  
  }, 
   “value”: { 
     “count”: 1  
  }  

 

   “_id”: { 
     “to_user”: 123, 
     “feed_type”: 1  
  }, 
   “value”: { 
     “count”: 2  
  }  

{
   “_id”: {
     “to_user”: 234,
     “feed_type”: 1
  },
   “value”: {
     “count”: 1
  }
}

{
   “_id”: {
     “to_user”: 123,
     “feed_type”: 8
  },
   “value”: {
     “count”: 1
  }
}

{
   “_id”: {
     “to_user”: 123,
     “feed_type”: 1
  },
   “value”: {
     “count”: 2
  }
}

 

以上只是简短的计算完毕,你能够完成复杂的标准总结编写复杂的reduce函数,能够追加查询条件,排序等等。

依赖mapReduce数据库管理函数(简单包装)
[php]
/**
 * mapReduce分组
 * 
 * @param string $table_name 表名(要操作的目的集结名卡塔尔
 * @param string $map 映射函数(生成键值对队列,作为 reduce 函数参数卡塔尔 
 * @param string $reduce 总结处理函数
 * @param array  $query 过滤条件 如:array(‘uid’=>123State of Qatar
 * @param array  $sort 排序
 * @param number $limit 节制的指标志录数
 * @param string $out 总结结果存放集结(不钦定则运用tmp_mr_res_$table_name, 1.8之上版本需点名卡塔尔国
 * @param bool   $keeptemp 是还是不是保留有的时候集合
 * @param string $finalize 最后管理函数
(对reduce重返结果举行末段收拾后存入结果会集卡塔尔
 * @param string $scope 向 map、reduce、finalize 导入外界js变量
 * @param bool   $jsMode
是还是不是压缩实施进度中BSON和JS的调换,暗中认可true(注:false时
BSON–>JS–>map–>BSON–>JS–>reduce–>BSON,可管理相当的大的mapreduce,//true时BSON–>js–>map–>reduce–>BSON卡塔尔(قطر‎
 * @param bool   $verbose 是或不是产生进一层详实的服务器日志
 * @param bool   $returnresult 是还是不是再次回到新的结果集
 * @param array  &$cmdresult 重回mp命令推行结果
array(“errmsg”=>””,”code”=>13606,”ok”=>0卡塔尔ok=1表示试行命令成功
 * @return 
 */ 
function
mapReduce($table_name,$map,$reduce,$query=null,$sort=null,$limit=0,$out=”,$keeptemp=true,$finalize=null,$scope=null,$jsMode=true,$verbose=true,$returnresult=true,&$cmdresult){ 
    if(empty($table_name) || empty($map) || empty($reduce)){ 
        return null; 
    } 
    $map = new MongoCode($map); 
    $reduce = new MongoCode($reduce); 
    if(empty($out)){ 
        $out = ‘tmp_mr_res_’.$table_name; 
    } 
    $cmd = array( 
            ‘mapreduce’ => $table_name, 
            ‘map’       => $map, 
            ‘reduce’    => $reduce, 
            ‘out’       =>$out 
    ); 
    if(!empty($query) && is_array($query)){ 
        array_push($cmd, array(‘query’=>$query)); 
    } 
    if(!empty($sort) && is_array($sort)){ 
        array_push($cmd, array(‘sort’=>$query)); 
    } 
    if(!empty($limit) && is_int($limit) && $limit>0){ 
        array_push($cmd, array(‘limit’=>$limit)); 
    } 
    if(!empty($keeptemp) && is_bool($keeptemp)){ 
        array_push($cmd, array(‘keeptemp’=>$keeptemp)); 
    } 
    if(!empty($finalize)){ 
        $finalize = new Mongocode($finalize); 
        array_push($cmd, array(‘finalize’=>$finalize)); 
    } 
    if(!empty($scope)){ 
        array_push($cmd, array(‘scope’=>$scope)); 
    } 
    if(!empty($jsMode) && is_bool($jsMode)){ 
        array_push($cmd, array(‘jsMode’=>$jsMode)); 
    } 
    if(!empty($verbose) && is_bool($verbose)){ 
        array_push($cmd, array(‘verbose’=>$verbose)); 
    } 
    $dbname = $this->curr_db_name; 
    $cmdresult = $this->mongo->$dbname->command($cmd); 
    if($returnresult){ 
        if($cmdresult && $cmdresult[‘ok’]==1){ 
            $result = $this->find($out, array()); 
        } 
    } 
    if($keeptemp==false){ 
        //删除群集  
        $this->mongo->$dbname->dropCollection($out); 
    } 
    return $result; 

    /**
     * mapReduce分组
     *
     * @param string $table_name 表名(要操作的对象会集名卡塔尔
     * @param string $map 映射函数(生成键值对队列,作为 reduce
函数参数卡塔尔国
     * @param string $reduce 总计管理函数
     * @param array  $query 过滤条件 如:array(‘uid’=>123卡塔尔国
     * @param array  $sort 排序
     * @param number $limit 限定的靶子记录数
     * @param string $out 总括结果存放集合(不钦定则采用tmp_mr_res_$table_name, 1.8上述版本需点名卡塔尔国
     * @param bool   $keeptemp 是或不是保留有时集合
     * @param string $finalize 最后管理函数
(对reduce重返结果开展最终收拾后存入结果集结卡塔尔(قطر‎
     * @param string $scope 向 map、reduce、finalize 导入外界js变量
     * @param bool   $jsMode
是不是压缩施行进度中BSON和JS的更动,私下认可true(注:false时
BSON–>JS–>map–>BSON–>JS–>reduce–>BSON,可管理相当的大的mapreduce,//true时BSON–>js–>map–>reduce–>BSON卡塔尔
     * @param bool   $verbose 是或不是产生越来越详细的服务器日志
     * @param bool   $returnresult 是不是重返新的结果集
     * @param array  &$cmdresult 重返mp命令试行结果
array(“errmsg”=>””,”code”=>13606,”ok”=>0卡塔尔ok=1表示实践命令成功
     * @return
     */
    function
mapReduce($table_name,$map,$reduce,$query=null,$sort=null,$limit=0,$out=”,$keeptemp=true,$finalize=null,$scope=null,$jsMode=true,$verbose=true,$returnresult=true,&$cmdresult){
     if(empty($table_name) || empty($map) || empty($reduce)){
      return null;
     }
     $map = new MongoCode($map);
     $reduce = new MongoCode($reduce);
     if(empty($out)){
      $out = ‘tmp_mr_res_’.$table_name;
     }
     $cmd = array(
       ‘mapreduce’ => $table_name,
       ‘map’       => $map,
       ‘reduce’    => $reduce,
       ‘out’  =>$out
     );
     if(!empty($query) && is_array($query)){
      array_push($cmd, array(‘query’=>$query));
     }
     if(!empty($sort) && is_array($sort)){
      array_push($cmd, array(‘sort’=>$query));
     }
     if(!empty($limit) && is_int($limit) && $limit>0){
      array_push($cmd, array(‘limit’=>$limit));
     }
     if(!empty($keeptemp) && is_bool($keeptemp)){
      array_push($cmd, array(‘keeptemp’=>$keeptemp));
     }
     if(!empty($finalize)){
      $finalize = new Mongocode($finalize);
      array_push($cmd, array(‘finalize’=>$finalize));
     }
     if(!empty($scope)){
      array_push($cmd, array(‘scope’=>$scope));
     }
     if(!empty($jsMode) && is_bool($jsMode)){
      array_push($cmd, array(‘jsMode’=>$jsMode));
     }
     if(!empty($verbose) && is_bool($verbose)){
      array_push($cmd, array(‘verbose’=>$verbose));
     }
     $dbname = $this->curr_db_name;
     $cmdresult = $this->mongo->$dbname->command($cmd);
     if($returnresult){
      if($cmdresult && $cmdresult[‘ok’]==1){
       $result = $this->find($out, array());
      }
     }
     if($keeptemp==false){
      //删除会集
      $this->mongo->$dbname->dropCollection($out);
     }
     return $result;
    }

 

by函数那样方便分组,可是MongoDB要落实分组也可以有3个法子: *
Mongodb三种…

发表评论

电子邮件地址不会被公开。 必填项已用*标注