PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/MapReduce.php

https://bitbucket.org/intel352/riiak
PHP | 331 lines | 134 code | 36 blank | 161 comment | 18 complexity | cd47f1c8c3c745bd861dd59cb605a1f8 MD5 | raw file
Possible License(s): Apache-2.0
  1. <?php
  2. namespace riiak;
  3. use \CComponent,
  4. \CJSON,
  5. \Exception,
  6. \Yii;
  7. /**
  8. * The MapReduce object allows you to build up and run a
  9. * map/reduce operation on Riak.
  10. * @package riiak
  11. */
  12. class MapReduce extends CComponent {
  13. /**
  14. * A Riak client object
  15. *
  16. * @var Riiak
  17. */
  18. public $client;
  19. /**
  20. * Phases to performs map/reduce operations
  21. *
  22. * @var array
  23. */
  24. public $phases = array();
  25. /**
  26. * Bucket name (string) or array of inputs
  27. * If bucket name, then all keys of bucket will be used as inputs (expensive)
  28. *
  29. * @var string|array
  30. */
  31. public $inputs = array();
  32. /**
  33. * Input mode specifies operating mode (e.g Bucket)
  34. *
  35. * @var string
  36. */
  37. public $inputMode;
  38. /**
  39. * List of key filters
  40. *
  41. * @var array
  42. */
  43. public $keyFilters = array();
  44. /**
  45. * @param Riiak $client A Riiak object
  46. */
  47. public function __construct(Riiak $client) {
  48. $this->client = $client;
  49. }
  50. /**
  51. * Adds object's bucket name and key will be added to m/r inputs
  52. *
  53. * @param \riiak\Object $obj
  54. * @return \riiak\MapReduce
  55. */
  56. public function addObject(Object $obj) {
  57. return $this->addBucketKeyData($obj->bucket->name, $obj->key, null);
  58. }
  59. /**
  60. * Adds bucket, key, and optional data to m/r inputs
  61. *
  62. * @param string $bucket Bucket name
  63. * @param string $key Key name
  64. * @param string $data
  65. * @return \riiak\MapReduce
  66. */
  67. public function addBucketKeyData($bucket, $key, $data = null) {
  68. if ($this->inputMode == 'bucket')
  69. throw new Exception('Already added a bucket, can\'t add an object.');
  70. $this->inputs[] = array((string) $bucket, (string) $key, (string) $data);
  71. return $this;
  72. }
  73. /**
  74. * Adds bucket to m/r inputs
  75. * Means all of the bucket's keys will be used as inputs (expensive)
  76. *
  77. * @param string $bucket Bucket name
  78. * @return \riiak\MapReduce
  79. */
  80. public function addBucket($bucket) {
  81. $this->inputMode = 'bucket';
  82. $this->inputs = (string) $bucket;
  83. return $this;
  84. }
  85. /**
  86. * Begin a map/reduce operation using a Search. This command will
  87. * return an error unless executed against a Riak Search cluster.
  88. *
  89. * @param string $bucket The Bucket to search
  90. * @param string $query The Query to execute. (Lucene syntax.)
  91. * @return \riiak\MapReduce
  92. */
  93. public function search($bucket, $query) {
  94. $this->inputs = array('module' => 'riak_search', 'function' => 'mapred_search', 'arg' => array((string) $bucket, (string) $query));
  95. return $this;
  96. }
  97. /**
  98. * Add a link phase to the map/reduce operation.
  99. *
  100. * @param string $bucket Default: '_' - all buckets
  101. * @param string $tag Default: '_' - all buckets
  102. * @param bool $keep Whether to keep results from this stage in map/reduce
  103. * @return \riiak\MapReduce
  104. */
  105. public function link($bucket = '_', $tag = '_', $keep = false) {
  106. $this->phases[] = new LinkPhase((string) $bucket, (string) $tag, $keep);
  107. return $this;
  108. }
  109. /**
  110. * Add a map phase to the map/reduce operation.
  111. *
  112. * @param mixed $function Erlang (array) or Javascript function call (string)
  113. * @param array $options Optional assoc array containing language|keep|arg
  114. * @return \riiak\MapReduce
  115. */
  116. public function map($function, array $options = array()) {
  117. return $this->addPhase('map', $function, $options);
  118. }
  119. /**
  120. * Add a reduce phase to the map/reduce operation.
  121. *
  122. * @param mixed $function Erlang (array) or Javascript function call (string)
  123. * @param array $options Optional assoc array containing language|keep|arg
  124. * @return \riiak\MapReduce
  125. */
  126. public function reduce($function, array $options = array()) {
  127. return $this->addPhase('reduce', $function, $options);
  128. }
  129. /**
  130. * Add a map/reduce phase
  131. *
  132. * @param string $phase Name of phase-type to add (e.g.: map, reduce)
  133. * @param mixed $function Erlang (array) or Javascript function call (string)
  134. * @param array $options Optional assoc array containing language|keep|arg
  135. * @return \riiak\MapReduce
  136. */
  137. public function addPhase($phase, $function, array $options = array()) {
  138. $language = is_array($function) ? 'erlang' : 'javascript';
  139. $options = array_merge(
  140. array('language' => $language, 'keep' => false, 'arg' => null), $options);
  141. $this->phases[] = new MapReducePhase((string) $phase,
  142. $function,
  143. $options['language'],
  144. $options['keep'],
  145. $options['arg']
  146. );
  147. return $this;
  148. }
  149. /**
  150. * Add a key filter to the map/reduce operation.
  151. * @see function keyFilterAnd
  152. *
  153. * @param array $filter
  154. * @return \riiak\MapReduce
  155. */
  156. public function keyFilter(array $filter) {
  157. return call_user_func_array(array($this, 'keyFilterAnd'), func_get_args());
  158. }
  159. /**
  160. * Add a key filter to the map/reduce operation.
  161. * If there are already existing filters, an "and" condition will be used
  162. * to combine them.
  163. *
  164. * @param array $filter
  165. * @return \riiak\MapReduce
  166. */
  167. public function keyFilterAnd(array $filter) {
  168. $args = func_get_args();
  169. array_unshift($args, 'and');
  170. return call_user_func_array(array($this, 'keyFilterOperator'), $args);
  171. }
  172. /**
  173. * Add a key filter to the map/reduce operation.
  174. * If there are already existing filters, an "or" condition will be used
  175. * to combine them.
  176. *
  177. * @param array $filter
  178. * @return \riiak\MapReduce
  179. */
  180. public function keyFilterOr(array $filter) {
  181. $args = func_get_args();
  182. array_unshift($args, 'or');
  183. return call_user_func_array(array($this, 'keyFilterOperator'), $args);
  184. }
  185. /**
  186. * Add a key filter to the map/reduce operation.
  187. * If there are already existing filters, the conditional operator will be
  188. * used to combine them.
  189. *
  190. * @param string $operator Typically "and" or "or"
  191. * @param array $filter
  192. * @return \riiak\MapReduce
  193. */
  194. public function keyFilterOperator($operator, $filter) {
  195. $filters = func_get_args();
  196. array_shift($filters);
  197. if ($this->input_mode != 'bucket')
  198. throw new Exception('Key filters can only be used in bucket mode');
  199. if (count($this->keyFilters) > 0)
  200. $this->keyFilters = array(array(
  201. $operator,
  202. $this->keyFilters,
  203. $filters
  204. ));
  205. else
  206. $this->keyFilters = $filters;
  207. return $this;
  208. }
  209. /**
  210. * Run the map/reduce operation. Returns array of results
  211. * or Link objects if last phase is link phase
  212. *
  213. * @param integer $timeout optional Timeout in milliseconds. Riak default is 60000 (60s).
  214. * @return array
  215. */
  216. public function run($timeout = null) {
  217. $numPhases = count($this->phases);
  218. $linkResultsFlag = false;
  219. /**
  220. * If there are no phases, then just echo the inputs back to the user.
  221. */
  222. if ($numPhases == 0) {
  223. $this->reduce(array('riak_kv_mapreduce', 'reduce_identity'));
  224. $numPhases = 1;
  225. $linkResultsFlag = true;
  226. }
  227. /**
  228. * Convert all phases to associative arrays. Also, if none of the
  229. * phases are accumulating, then set the last one to accumulate.
  230. */
  231. $keepFlag = false;
  232. $query = array();
  233. for ($i = 0; $i < $numPhases; $i++) {
  234. $phase = $this->phases[$i];
  235. if ($i == ($numPhases - 1) && !$keepFlag)
  236. $phase->keep = true;
  237. if ($phase->keep)
  238. $keepFlag = true;
  239. $query[] = $phase->toArray();
  240. }
  241. /**
  242. * Add key filters if applicable
  243. */
  244. if ($this->inputMode == 'bucket' && count($this->keyFilters) > 0) {
  245. $this->inputs = array(
  246. 'bucket' => $this->inputs,
  247. 'key_filters' => $this->keyFilters
  248. );
  249. }
  250. /**
  251. * Construct the job, optionally set the timeout
  252. */
  253. $job = array('inputs' => $this->inputs, 'query' => $query);
  254. if ($timeout != null)
  255. $job['timeout'] = $timeout;
  256. $content = CJSON::encode($job);
  257. /**
  258. * Execute the request
  259. */
  260. Yii::trace('Running Map/Reduce query', 'ext.riiak.MapReduce');
  261. $transport = $this->client->transport;
  262. $response = $transport->post($transport->buildMapReducePath(), array(), $content);
  263. /**
  264. * Verify that we got one of the expected statuses. Otherwise, throw an exception
  265. */
  266. try {
  267. $transport->validateResponse($response, 'mapReduce');
  268. }catch(\Exception $e) {
  269. throw new \Exception($e . PHP_EOL . PHP_EOL . 'Job Request: '. $content . PHP_EOL . PHP_EOL
  270. . 'Response: '. \CVarDumper::dumpAsString($response), $e->getCode(), $e);
  271. }
  272. $result = CJSON::decode($response['body']);
  273. /**
  274. * If the last phase is NOT a link phase, then return the result.
  275. */
  276. $linkResultsFlag |= ( end($this->phases) instanceof LinkPhase);
  277. /**
  278. * If we don't need to link results, then just return.
  279. */
  280. if (!$linkResultsFlag)
  281. return $result;
  282. /**
  283. * Otherwise, if the last phase IS a link phase, then convert the
  284. * results to Link objects.
  285. */
  286. $a = array();
  287. foreach ($result as $r) {
  288. $tag = isset($r[2]) ? $r[2] : null;
  289. $link = new Link($r[0], $r[1], $tag);
  290. $link->client = $this->client;
  291. $a[] = $link;
  292. }
  293. return $a;
  294. }
  295. }