/** * 执行map reduce操作,为了防止数据量过大,导致无法完成mapreduce,统一采用集合的方式,取代内存方式 * 内存方式,不允许执行过程的数据量量超过物理内存的10%,故无法进行大数量分析工作。 * * @param array $command */ public function mapReduce($out = null, $map, $reduce, $query = array(), $finalize = null, $method = 'replace', $scope = null, $sort = array('$natural' => 1), $limit = null) { if ($out == null) { $out = md5(serialize(func_get_args())); } try { // map reduce执行锁管理开始 $locks = new self($this->_configInstance, 'locks', DB_MAPREDUCE, $this->_cluster); $locks->setReadPreference(\MongoClient::RP_PRIMARY_PREFERRED); $checkLock = function ($out) use($locks) { $check = $locks->findOne(array('out' => $out)); if ($check == null) { $locks->insert(array('out' => $out, 'isRunning' => true, 'expire' => new \MongoDate(time() + 300))); return false; } else { if (isset($check['isRunning']) && $check['isRunning']) { return true; } if ($check['isRunning'] && isset($check['expire']) && $check['expire'] instanceof \MongoDate) { if ($check['expire']->sec > time()) { return true; } else { $releaseLock($out); return false; } } $locks->update(array('out' => $out), array('$set' => array('isRunning' => true, 'expire' => new \MongoDate(time() + 300)))); return false; } }; $releaseLock = function ($out, $rst = null) use($locks) { return $locks->update(array('out' => $out), array('$set' => array('isRunning' => false, 'rst' => is_string($rst) ? $rst : Json::encode($rst)))); }; $failure = function ($code, $msg) { if (is_array($msg)) { $msg = Json::encode($msg); } return array('ok' => 0, 'code' => $code, 'msg' => $msg); }; // map reduce执行锁管理结束 if (!$checkLock($out)) { $command = array(); $command['mapreduce'] = $this->_collection; $command['map'] = $map instanceof \MongoCode ? $map : new \MongoCode($map); $command['reduce'] = $reduce instanceof \MongoCode ? $reduce : new \MongoCode($reduce); $command['query'] = $this->appendQuery($query); if (!empty($finalize)) { $command['finalize'] = $finalize instanceof \MongoCode ? $finalize : new \MongoCode($finalize); } if (!empty($sort)) { $command['sort'] = $sort; } if (!empty($limit)) { $command['limit'] = $limit; } if (!empty($scope)) { $command['scope'] = $scope; } $command['verbose'] = true; if (!in_array($method, array('replace', 'merge', 'reduce'), true)) { $method = 'replace'; } $command['out'] = array($method => $out, 'db' => DB_MAPREDUCE, 'sharded' => false, 'nonAtomic' => in_array($method, array('merge', 'reduce'), true) ? true : false); $rst = $this->command($command); $releaseLock($out, $rst); if ($rst['ok'] == 1) { if ($rst['counts']['emit'] > 0 && $rst['counts']['output'] > 0) { $outMongoCollection = new self($this->_configInstance, $out, DB_MAPREDUCE, $this->_cluster); $outMongoCollection->setNoAppendQuery(true); return $outMongoCollection; } return $failure(500, $rst['counts']); } else { return $failure(501, $rst); } } else { return $failure(502, '程序正在执行中,请勿频繁尝试'); } } catch (\Exception $e) { if (isset($releaseLock) && isset($failure)) { $releaseLock($out, exceptionMsg($e)); return $failure(503, exceptionMsg($e)); } var_dump(exceptionMsg($e)); } }