Search.Class.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. <?php
  2. namespace Mall\Framework\Core;
  3. use Mall\Framework\SearchClient\Client;
  4. use Mall\Framework\SearchClient\Transport;
  5. use Exception;
  6. class Search
  7. {
  8. /**
  9. * @var array
  10. */
  11. private $config;
  12. /**
  13. * SearchClient 客户端
  14. *
  15. * @var Client
  16. */
  17. private $connection = null;
  18. /**
  19. * @var int
  20. */
  21. private $serverId;
  22. /**
  23. * @var array
  24. */
  25. private static $badServerIds = array();
  26. /**
  27. * @var object
  28. */
  29. private static $_instance;
  30. /**
  31. * 构造函数
  32. *
  33. * @param $config
  34. */
  35. public function __construct(array $config)
  36. {
  37. $this->config = $config;
  38. }
  39. /**
  40. * 从服务器组中获取一个可用的ES服务器信息
  41. *
  42. * @return array
  43. * @throws Exception
  44. */
  45. private function getServer()
  46. {
  47. $config = $this->config;
  48. $serverId = null;
  49. if (empty(self::$badServerIds)) {
  50. $serverId = array_rand($config['servers']);
  51. } else {
  52. foreach ($config['servers'] as $id => $val) {
  53. if (in_array($id, self::$badServerIds)) {
  54. continue;
  55. }
  56. $serverId = $id;
  57. break;
  58. }
  59. }
  60. if ($serverId === null) {
  61. throw new Exception('服务暂时不可用,请稍后再试!');
  62. }
  63. $this->serverId = $serverId;
  64. return array(
  65. 'protocol' => $config['protocol'],
  66. 'timeout' => $config['timeout'],
  67. 'index' => $config['index'],
  68. 'type' => $config['type'],
  69. 'servers' => $config['servers'][$serverId]
  70. );
  71. }
  72. /**
  73. * 设置Index索引
  74. *
  75. * @param $index
  76. * @return Search|null
  77. */
  78. public function setIndex($index)
  79. {
  80. $this->config['index'] = $index;
  81. return is_object($this->getConnection()->setIndex($index)) ? $this : null;
  82. }
  83. /**
  84. * 设置Type
  85. *
  86. * @param $type
  87. * @return Search|null
  88. */
  89. public function setType($type)
  90. {
  91. $this->config['type'] = $type;
  92. return is_object($this->getConnection()->setType($type)) ? $this : null;
  93. }
  94. /**
  95. * 创建Index索引
  96. *
  97. * @param string $index 索引名称
  98. * @param array $mappings options
  99. * @return mixed
  100. */
  101. public function createBase($index, $mappings)
  102. {
  103. return $this->getConnection()->createBase($index, $mappings);
  104. }
  105. /**
  106. * 删除Index索引
  107. *
  108. * @param string $index 索引名称
  109. * @return mixed
  110. */
  111. public function deleteBase($index)
  112. {
  113. return $this->getConnection()->deleteBase($index);
  114. }
  115. /**
  116. * 根据 ID 获取文档
  117. *
  118. * @param $id
  119. * @param bool|false $verbose
  120. * @return array
  121. */
  122. public function get($id, $verbose = false)
  123. {
  124. $this->checkIndexType();
  125. return $this->getConnection()->get($id, $verbose);
  126. }
  127. /**
  128. * 创建索引
  129. *
  130. * @param string $document 文档格式
  131. * @param string $id 文档ID
  132. * @param array $options 自定义选项
  133. * @return mixed
  134. */
  135. public function index($document, $id, $options = array())
  136. {
  137. $this->checkIndexType();
  138. $this->log('index', sprintf(
  139. 'domain:[%s] uri:[%s] contentid:[%s] %s %s',
  140. empty($_SERVER['HTTP_HOST']) ? '' : $_SERVER['HTTP_HOST'],
  141. empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'],
  142. $id,
  143. var_export($document, true),
  144. var_export($options, true)
  145. ));
  146. return $this->getConnection()->index($document, $id, $options);
  147. }
  148. /**
  149. * 更新索引
  150. *
  151. * @param string $document 文档
  152. * @param string $id 文档ID
  153. * @param array $options 自定义选项
  154. * @return mixed
  155. */
  156. public function update($document, $id, $options = array())
  157. {
  158. $this->checkIndexType();
  159. $this->log('update', sprintf(
  160. 'domain:[%s] uri:[%s] contentid:[%s] %s %s',
  161. empty($_SERVER['HTTP_HOST']) ? '' : $_SERVER['HTTP_HOST'],
  162. empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'],
  163. $id,
  164. var_export($document, true),
  165. var_export($options, true)
  166. ));
  167. return $this->getConnection()->index($document, $id, $options);
  168. }
  169. /**
  170. * 删除索引
  171. *
  172. * @param string $id 索引ID
  173. * @return null
  174. */
  175. public function delete($id)
  176. {
  177. if (!$id) return null;
  178. $this->checkIndexType();
  179. $this->log('delete', sprintf(
  180. 'domain:[%s] uri:[%s] contentid:[%s]',
  181. empty($_SERVER['HTTP_HOST']) ? '' : $_SERVER['HTTP_HOST'],
  182. empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'],
  183. $id
  184. ));
  185. return $this->getConnection()->delete($id);
  186. }
  187. /**
  188. * 搜索接口
  189. *
  190. * @param array $query DSL查询格式
  191. * @param array $options 选项
  192. * @return array
  193. * @throws Exception
  194. */
  195. public function search($query = array(), array $options = array())
  196. {
  197. try {
  198. $client = $this->getConnection()->search($query, $options);
  199. return empty($client) ? array() : $client;
  200. } catch (Transport\HTTPException $e) {
  201. self::$badServerIds[] = $this->serverId;
  202. $this->connection = Client::connection($this->getServer());
  203. return $this->search($query, $options);
  204. }
  205. }
  206. /**
  207. * 搜索服务链接
  208. *
  209. * @return Client
  210. * @throws Exception
  211. */
  212. public function getConnection()
  213. {
  214. $key = md5(json_encode($this->config));
  215. if (!isset(self::$_instance[$key]) || !self::$_instance[$key] instanceof Client) {
  216. if ($this->connection) {
  217. return $this->connection;
  218. }
  219. $connection = Client::connection($this->getServer());
  220. $connection->setIndex($this->config['index']);
  221. $connection->setType($this->config['type']);
  222. self::$_instance[$key] = $this->connection = $connection;
  223. }
  224. return self::$_instance[$key];
  225. }
  226. private function checkIndexType()
  227. {
  228. $connection = $this->getConnection();
  229. $index = $connection->getIndex();
  230. if (empty($index)) {
  231. throw new \RuntimeException('Invalid search index');
  232. }
  233. $type = $connection->getType();
  234. if (empty($type)) {
  235. throw new \RuntimeException('Invalid search index type');
  236. }
  237. return true;
  238. }
  239. protected function log($type, $message)
  240. {
  241. if (!defined('DEBUG_MODE') || DEBUG_MODE != 1) {
  242. return;
  243. }
  244. $temp_dir = sys_get_temp_dir();
  245. if (!is_writable($temp_dir)) {
  246. return;
  247. }
  248. $log_file = sprintf('%s/search_%s.log', $temp_dir, $type);
  249. $log = sprintf(
  250. '%s index[%s] type[%s] %s',
  251. date('r'),
  252. $this->config['index'],
  253. $this->config['type'],
  254. $message
  255. );
  256. file_put_contents($log_file, $log . PHP_EOL, FILE_APPEND);
  257. }
  258. }