PageRenderTime 41ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/rediska/vendor/rediska/Rediska/Command/CompareSortedSets.php

https://bitbucket.org/sudak/rating
PHP | 190 lines | 130 code | 27 blank | 33 comment | 16 complexity | 441ffd89f480535c2249577f5ceba30d MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Abstract class for union and intersection of sorted sets
  4. *
  5. * @author Ivan Shumkov
  6. * @package Rediska
  7. * @subpackage Commands
  8. * @version @package_version@
  9. * @link http://rediska.geometria-lab.net
  10. * @license http://www.opensource.org/licenses/bsd-license.php
  11. */
  12. abstract class Rediska_Command_CompareSortedSets extends Rediska_Command_Abstract
  13. {
  14. const SUM = 'sum';
  15. const MAX = 'max';
  16. const MIN = 'min';
  17. /**
  18. * Supported version
  19. *
  20. * @var string
  21. */
  22. protected $_version = '1.3.12';
  23. protected $_command;
  24. protected $_storeConnection;
  25. protected $_keys = array();
  26. protected $_weights = array();
  27. /**
  28. * Create command
  29. *
  30. * @param array $keys Array of key names or associative array with weights
  31. * @param string $storeKey Result sorted set key name
  32. * @param string $aggregation Aggregation method: SUM (for default), MIN, MAX.
  33. * @return Rediska_Connection_Exec
  34. */
  35. public function create(array $keys, $storeKey, $aggregation = self::SUM)
  36. {
  37. if (empty($keys)) {
  38. throw new Rediska_Command_Exception('You must specify sorted sets');
  39. }
  40. // With weights?
  41. $withWeights = false;
  42. foreach($keys as $keyOrIndex => $keyOrWeight) {
  43. if (is_string($keyOrIndex)) {
  44. $this->_weights = $keys;
  45. $keys = array_keys($keys);
  46. $withWeights = true;
  47. break;
  48. }
  49. }
  50. $connections = array();
  51. $keysByConnections = array();
  52. foreach ($keys as $key) {
  53. $connection = $this->_rediska->getConnectionByKeyName($key);
  54. $connectionAlias = $connection->getAlias();
  55. if (!array_key_exists($connectionAlias, $connections)) {
  56. $connections[$connectionAlias] = $connection;
  57. $keysByConnections[$connectionAlias] = array();
  58. }
  59. $keysByConnections[$connectionAlias][] = $key;
  60. }
  61. // If only one connection, compare by redis
  62. if (count($connections) == 1) {
  63. $connectionValues = array_values($connections);
  64. $connection = $connectionValues[0];
  65. $storeConnection = $this->_rediska->getConnectionByKeyName($storeKey);
  66. if ($storeConnection === $connection) {
  67. $command = array($this->_command,
  68. $this->_rediska->getOption('namespace') . $storeKey,
  69. count($keys));
  70. foreach($keys as $key) {
  71. $command[] = $this->_rediska->getOption('namespace') . $key;
  72. }
  73. if ($withWeights) {
  74. $command[] = 'WEIGHTS';
  75. $command = array_merge($command, $this->_weights);
  76. }
  77. if (strtolower($aggregation) != self::SUM) {
  78. $command[] = 'AGGREGATE';
  79. $command[] = strtoupper($aggregation);
  80. }
  81. return new Rediska_Connection_Exec($connection, $command);
  82. }
  83. }
  84. // Compare by hand
  85. // Set default weights
  86. if (!$withWeights) {
  87. $this->_weights = array_fill_keys($keys, 1);
  88. }
  89. $this->setAtomic(false);
  90. $commands = array();
  91. foreach($keysByConnections as $connectionAlias => $keys) {
  92. foreach($keys as $key) {
  93. $this->_keys[] = $key;
  94. $command = array('ZRANGE',
  95. $this->_rediska->getOption('namespace') . $key,
  96. 0,
  97. -1,
  98. 'WITHSCORES');
  99. $commands[] = new Rediska_Connection_Exec($connections[$connectionAlias], $command);
  100. }
  101. }
  102. return $commands;
  103. }
  104. /**
  105. * Parse responses
  106. *
  107. * @param array $responses
  108. * @return integer
  109. */
  110. public function parseResponses($responses)
  111. {
  112. if ($this->isAtomic()) {
  113. return $responses[0];
  114. } else {
  115. $sets = array();
  116. $valuesWithScores = array();
  117. foreach ($this->_keys as $key) {
  118. $sets[$key] = array();
  119. $response = current($responses);
  120. next($responses);
  121. $isValue = true;
  122. foreach ($response as $valueOrScore) {
  123. if ($isValue) {
  124. $value = $valueOrScore;
  125. $sets[$key][] = $value;
  126. if (!isset($valuesWithScores[$value])) {
  127. $valuesWithScores[$value] = array();
  128. }
  129. } else {
  130. $score = $valueOrScore;
  131. $valuesWithScores[$value][] = $score * $this->_weights[$key];
  132. }
  133. $isValue = !$isValue;
  134. }
  135. }
  136. $aggregation = strtolower($this->aggregation);
  137. $pipeline = $this->_rediska->pipeline();
  138. $count = 0;
  139. foreach($this->_compareSets($sets) as $value) {
  140. $scores = $valuesWithScores[$value];
  141. switch ($aggregation) {
  142. case self::SUM:
  143. $score = array_sum($scores);
  144. break;
  145. case self::MIN:
  146. $score = min($scores);
  147. break;
  148. case self::MAX:
  149. $score = max($scores);
  150. break;
  151. default:
  152. throw new Rediska_Command_Exception('Unknown aggregation method ' . $this->aggregation);
  153. }
  154. $value = $this->_rediska->getSerializer()->unserialize($value);
  155. $pipeline->addToSortedSet($this->storeKey, $value, $score);
  156. $count++;
  157. }
  158. $pipeline->execute();
  159. return $count;
  160. }
  161. }
  162. abstract protected function _compareSets($sets);
  163. }