Elasticsearch.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Elasticsearch\Model\Client;
  7. use Magento\Framework\Exception\LocalizedException;
  8. use Magento\AdvancedSearch\Model\Client\ClientInterface;
  9. /**
  10. * Elasticsearch client
  11. */
  12. class Elasticsearch implements ClientInterface
  13. {
  14. /**
  15. * Elasticsearch Client instances
  16. *
  17. * @var \Elasticsearch\Client[]
  18. */
  19. private $client;
  20. /**
  21. * @var array
  22. */
  23. private $clientOptions;
  24. /**
  25. * @var bool
  26. */
  27. private $pingResult;
  28. /**
  29. * Initialize Elasticsearch Client
  30. *
  31. * @param array $options
  32. * @param \Elasticsearch\Client|null $elasticsearchClient
  33. * @throws LocalizedException
  34. */
  35. public function __construct(
  36. $options = [],
  37. $elasticsearchClient = null
  38. ) {
  39. if (empty($options['hostname']) || ((!empty($options['enableAuth']) &&
  40. ($options['enableAuth'] == 1)) && (empty($options['username']) || empty($options['password'])))) {
  41. throw new LocalizedException(
  42. __('The search failed because of a search engine misconfiguration.')
  43. );
  44. }
  45. if (!($elasticsearchClient instanceof \Elasticsearch\Client)) {
  46. $config = $this->buildConfig($options);
  47. $elasticsearchClient = \Elasticsearch\ClientBuilder::fromConfig($config, true);
  48. }
  49. $this->client[getmypid()] = $elasticsearchClient;
  50. $this->clientOptions = $options;
  51. }
  52. /**
  53. * Get Elasticsearch Client
  54. *
  55. * @return \Elasticsearch\Client
  56. */
  57. private function getClient()
  58. {
  59. $pid = getmypid();
  60. if (!isset($this->client[$pid])) {
  61. $config = $this->buildConfig($this->clientOptions);
  62. $this->client[$pid] = \Elasticsearch\ClientBuilder::fromConfig($config, true);
  63. }
  64. return $this->client[$pid];
  65. }
  66. /**
  67. * Ping the Elasticsearch client
  68. *
  69. * @return bool
  70. */
  71. public function ping()
  72. {
  73. if ($this->pingResult === null) {
  74. $this->pingResult = $this->getClient()->ping(['client' => ['timeout' => $this->clientOptions['timeout']]]);
  75. }
  76. return $this->pingResult;
  77. }
  78. /**
  79. * Validate connection params
  80. *
  81. * @return bool
  82. */
  83. public function testConnection()
  84. {
  85. return $this->ping();
  86. }
  87. /**
  88. * @param array $options
  89. * @return array
  90. */
  91. private function buildConfig($options = [])
  92. {
  93. $host = preg_replace('/http[s]?:\/\//i', '', $options['hostname']);
  94. $protocol = parse_url($options['hostname'], PHP_URL_SCHEME);
  95. if (!$protocol) {
  96. $protocol = 'http';
  97. }
  98. if (!empty($options['port'])) {
  99. $host .= ':' . $options['port'];
  100. }
  101. if (!empty($options['enableAuth']) && ($options['enableAuth'] == 1)) {
  102. $host = sprintf('%s://%s:%s@%s', $protocol, $options['username'], $options['password'], $host);
  103. }
  104. $options['hosts'] = [$host];
  105. return $options;
  106. }
  107. /**
  108. * Performs bulk query over Elasticsearch index
  109. *
  110. * @param array $query
  111. * @return void
  112. */
  113. public function bulkQuery($query)
  114. {
  115. $this->getClient()->bulk($query);
  116. }
  117. /**
  118. * Creates an Elasticsearch index.
  119. *
  120. * @param string $index
  121. * @param array $settings
  122. * @return void
  123. */
  124. public function createIndex($index, $settings)
  125. {
  126. $this->getClient()->indices()->create([
  127. 'index' => $index,
  128. 'body' => $settings,
  129. ]);
  130. }
  131. /**
  132. * Delete an Elasticsearch index.
  133. *
  134. * @param string $index
  135. * @return void
  136. */
  137. public function deleteIndex($index)
  138. {
  139. $this->getClient()->indices()->delete(['index' => $index]);
  140. }
  141. /**
  142. * Check if index is empty.
  143. *
  144. * @param string $index
  145. * @return bool
  146. */
  147. public function isEmptyIndex($index)
  148. {
  149. $stats = $this->getClient()->indices()->stats(['index' => $index, 'metric' => 'docs']);
  150. if ($stats['indices'][$index]['primaries']['docs']['count'] == 0) {
  151. return true;
  152. }
  153. return false;
  154. }
  155. /**
  156. * Updates alias.
  157. *
  158. * @param string $alias
  159. * @param string $newIndex
  160. * @param string $oldIndex
  161. * @return void
  162. */
  163. public function updateAlias($alias, $newIndex, $oldIndex = '')
  164. {
  165. $params['body'] = ['actions' => []];
  166. if ($oldIndex) {
  167. $params['body']['actions'][] = ['remove' => ['alias' => $alias, 'index' => $oldIndex]];
  168. }
  169. if ($newIndex) {
  170. $params['body']['actions'][] = ['add' => ['alias' => $alias, 'index' => $newIndex]];
  171. }
  172. $this->getClient()->indices()->updateAliases($params);
  173. }
  174. /**
  175. * Checks whether Elasticsearch index exists
  176. *
  177. * @param string $index
  178. * @return bool
  179. */
  180. public function indexExists($index)
  181. {
  182. return $this->getClient()->indices()->exists(['index' => $index]);
  183. }
  184. /**
  185. * @param string $alias
  186. * @param string $index
  187. *
  188. * @return bool
  189. */
  190. public function existsAlias($alias, $index = '')
  191. {
  192. $params = ['name' => $alias];
  193. if ($index) {
  194. $params['index'] = $index;
  195. }
  196. return $this->getClient()->indices()->existsAlias($params);
  197. }
  198. /**
  199. * @param string $alias
  200. *
  201. * @return array
  202. */
  203. public function getAlias($alias)
  204. {
  205. return $this->getClient()->indices()->getAlias(['name' => $alias]);
  206. }
  207. /**
  208. * Add mapping to Elasticsearch index
  209. *
  210. * @param array $fields
  211. * @param string $index
  212. * @param string $entityType
  213. * @return void
  214. */
  215. public function addFieldsMapping(array $fields, $index, $entityType)
  216. {
  217. $params = [
  218. 'index' => $index,
  219. 'type' => $entityType,
  220. 'body' => [
  221. $entityType => [
  222. '_all' => [
  223. 'enabled' => true,
  224. 'type' => 'string'
  225. ],
  226. 'properties' => [],
  227. 'dynamic_templates' => [
  228. [
  229. 'price_mapping' => [
  230. 'match' => 'price_*',
  231. 'match_mapping' => 'string',
  232. 'mapping' => [
  233. 'type' => 'float'
  234. ],
  235. ],
  236. ],
  237. [
  238. 'string_mapping' => [
  239. 'match' => '*',
  240. 'match_mapping' => 'string',
  241. 'mapping' => [
  242. 'type' => 'string',
  243. 'index' => 'no'
  244. ],
  245. ],
  246. ],
  247. [
  248. 'position_mapping' => [
  249. 'match' => 'position_*',
  250. 'match_mapping' => 'string',
  251. 'mapping' => [
  252. 'type' => 'int'
  253. ],
  254. ],
  255. ],
  256. ],
  257. ],
  258. ],
  259. ];
  260. foreach ($fields as $field => $fieldInfo) {
  261. $params['body'][$entityType]['properties'][$field] = $fieldInfo;
  262. }
  263. $this->getClient()->indices()->putMapping($params);
  264. }
  265. /**
  266. * Delete mapping in Elasticsearch index
  267. *
  268. * @param string $index
  269. * @param string $entityType
  270. * @return void
  271. */
  272. public function deleteMapping($index, $entityType)
  273. {
  274. $this->getClient()->indices()->deleteMapping([
  275. 'index' => $index,
  276. 'type' => $entityType,
  277. ]);
  278. }
  279. /**
  280. * Execute search by $query
  281. *
  282. * @param array $query
  283. * @return array
  284. */
  285. public function query($query)
  286. {
  287. return $this->getClient()->search($query);
  288. }
  289. /**
  290. * Execute suggest query
  291. *
  292. * @param array $query
  293. * @return array
  294. */
  295. public function suggest($query)
  296. {
  297. return $this->getClient()->suggest($query);
  298. }
  299. }