/** * Performs aggregation using Mongo "map reduce" mechanism. * Note: this function will not return the aggregation result, instead it will * write it inside the another Mongo collection specified by "out" parameter. * For example: * * ~~~ * $customerCollection = Yii::$app->mongo->getCollection('customer'); * $resultCollectionName = $customerCollection->mapReduce( * 'function () {emit(this.status, this.amount)}', * 'function (key, values) {return Array.sum(values)}', * 'mapReduceOut', * ['status' => 3] * ); * $query = new Query(); * $results = $query->from($resultCollectionName)->all(); * ~~~ * * @param \MongoCode|string $map function, which emits map data from collection. * Argument will be automatically cast to [[\MongoCode]]. * @param \MongoCode|string $reduce function that takes two arguments (the map key * and the map values) and does the aggregation. * Argument will be automatically cast to [[\MongoCode]]. * @param string|array $out output collection name. It could be a string for simple output * ('outputCollection'), or an array for parametrized output (['merge' => 'outputCollection']). * You can pass ['inline' => true] to fetch the result at once without temporary collection usage. * @param array $condition criteria for including a document in the aggregation. * @param array $options additional optional parameters to the mapReduce command. Valid options include: * - sort - array - key to sort the input documents. The sort key must be in an existing index for this collection. * - limit - the maximum number of documents to return in the collection. * - finalize - function, which follows the reduce method and modifies the output. * - scope - array - specifies global variables that are accessible in the map, reduce and finalize functions. * - jsMode - boolean -Specifies whether to convert intermediate data into BSON format between the execution of the map and reduce functions. * - verbose - boolean - specifies whether to include the timing information in the result information. * @return string|array the map reduce output collection name or output results. * @throws Exception on failure. */ public function mapReduce($map, $reduce, $out, $condition = [], $options = []) { $command = ['mapReduce' => $this->getName(), 'map' => $map, 'reduce' => $reduce, 'out' => $out]; if (!empty($condition)) { $command['query'] = $this->buildCondition($condition); } $command = new \MongoDB\Driver\Command(array_merge($command, $options)); $token = $this->composeLogToken('mapReduce', [$map, $reduce, $out]); Yii::info($token, __METHOD__); try { Yii::beginProfile($token, __METHOD__); $result = $this->mongoManager->executeCommand($this->dbName, $command); $this->tryResultError($result); Yii::endProfile($token, __METHOD__); $row = MongoHelper::cursorFirst($result); return isset($row['result']) ? $row['result'] : $row['results']; } catch (\Exception $e) { Yii::endProfile($token, __METHOD__); throw new Exception($e->getMessage(), (int) $e->getCode(), $e); } }
/** * Executes Mongo command. * @param array $command command specification. * @param array $options options in format: "name" => "value" * @return array database response. * @throws Exception on failure. */ public function executeCommand($command, $options = []) { $token = $this->getName() . '.$cmd(' . Json::encode($command) . ', ' . Json::encode($options) . ')'; Yii::info($token, __METHOD__); try { Yii::beginProfile($token, __METHOD__); $command = new \MongoDB\Driver\Command($command); $result = $this->mongoManager->executeCommand($this->dbName, $command); $this->tryResultError($result); Yii::endProfile($token, __METHOD__); return MongoHelper::cursorFirst($result); } catch (\Exception $e) { Yii::endProfile($token, __METHOD__); throw new Exception($e->getMessage(), (int) $e->getCode(), $e); } }
/** * @param \MongoDB\Driver\Cursor $cursor Mongo cursor instance to fetch data from. * @param boolean $all whether to fetch all rows or only first one. * @param string|callable $indexBy value to index by. * @return array|boolean result. * @see Query::fetchRows() */ protected function fetchRowsInternal($cursor, $all, $indexBy) { if ($all) { $rows = []; foreach ($cursor as $row) { $rows[] = MongoHelper::resultToArray($row); } return $rows; } else { foreach ($cursor as $row) { break; } return isset($row) ? MongoHelper::resultToArray($row) : false; } }