Elasticsearch.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Elasticsearch6\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. * Build config.
  89. *
  90. * @param array $options
  91. * @return array
  92. */
  93. private function buildConfig($options = [])
  94. {
  95. $host = preg_replace('/http[s]?:\/\//i', '', $options['hostname']);
  96. $protocol = parse_url($options['hostname'], PHP_URL_SCHEME);
  97. if (!$protocol) {
  98. $protocol = 'http';
  99. }
  100. if (!empty($options['port'])) {
  101. $host .= ':' . $options['port'];
  102. }
  103. if (!empty($options['enableAuth']) && ($options['enableAuth'] == 1)) {
  104. $host = sprintf('%s://%s:%s@%s', $protocol, $options['username'], $options['password'], $host);
  105. }
  106. $options['hosts'] = [$host];
  107. return $options;
  108. }
  109. /**
  110. * Performs bulk query over Elasticsearch index
  111. *
  112. * @param array $query
  113. * @return void
  114. */
  115. public function bulkQuery($query)
  116. {
  117. $this->getClient()->bulk($query);
  118. }
  119. /**
  120. * Creates an Elasticsearch index.
  121. *
  122. * @param string $index
  123. * @param array $settings
  124. * @return void
  125. */
  126. public function createIndex($index, $settings)
  127. {
  128. $this->getClient()->indices()->create([
  129. 'index' => $index,
  130. 'body' => $settings,
  131. ]);
  132. }
  133. /**
  134. * Delete an Elasticsearch index.
  135. *
  136. * @param string $index
  137. * @return void
  138. */
  139. public function deleteIndex($index)
  140. {
  141. $this->getClient()->indices()->delete(['index' => $index]);
  142. }
  143. /**
  144. * Check if index is empty.
  145. *
  146. * @param string $index
  147. * @return bool
  148. */
  149. public function isEmptyIndex($index)
  150. {
  151. $stats = $this->getClient()->indices()->stats(['index' => $index, 'metric' => 'docs']);
  152. if ($stats['indices'][$index]['primaries']['docs']['count'] == 0) {
  153. return true;
  154. }
  155. return false;
  156. }
  157. /**
  158. * Updates alias.
  159. *
  160. * @param string $alias
  161. * @param string $newIndex
  162. * @param string $oldIndex
  163. * @return void
  164. */
  165. public function updateAlias($alias, $newIndex, $oldIndex = '')
  166. {
  167. $params['body'] = ['actions' => []];
  168. if ($oldIndex) {
  169. $params['body']['actions'][] = ['remove' => ['alias' => $alias, 'index' => $oldIndex]];
  170. }
  171. if ($newIndex) {
  172. $params['body']['actions'][] = ['add' => ['alias' => $alias, 'index' => $newIndex]];
  173. }
  174. $this->getClient()->indices()->updateAliases($params);
  175. }
  176. /**
  177. * Checks whether Elasticsearch index exists
  178. *
  179. * @param string $index
  180. * @return bool
  181. */
  182. public function indexExists($index)
  183. {
  184. return $this->getClient()->indices()->exists(['index' => $index]);
  185. }
  186. /**
  187. * Exists alias.
  188. *
  189. * @param string $alias
  190. * @param string $index
  191. * @return bool
  192. */
  193. public function existsAlias($alias, $index = '')
  194. {
  195. $params = ['name' => $alias];
  196. if ($index) {
  197. $params['index'] = $index;
  198. }
  199. return $this->getClient()->indices()->existsAlias($params);
  200. }
  201. /**
  202. * Get alias.
  203. *
  204. * @param string $alias
  205. * @return array
  206. */
  207. public function getAlias($alias)
  208. {
  209. return $this->getClient()->indices()->getAlias(['name' => $alias]);
  210. }
  211. /**
  212. * Add mapping to Elasticsearch index
  213. *
  214. * @param array $fields
  215. * @param string $index
  216. * @param string $entityType
  217. * @return void
  218. */
  219. public function addFieldsMapping(array $fields, $index, $entityType)
  220. {
  221. $params = [
  222. 'index' => $index,
  223. 'type' => $entityType,
  224. 'body' => [
  225. $entityType => [
  226. 'properties' => [
  227. '_search' => [
  228. 'type' => 'text'
  229. ],
  230. ],
  231. 'dynamic_templates' => [
  232. [
  233. 'price_mapping' => [
  234. 'match' => 'price_*',
  235. 'match_mapping_type' => 'string',
  236. 'mapping' => [
  237. 'type' => 'float',
  238. 'store' => true,
  239. ],
  240. ],
  241. ],
  242. [
  243. 'string_mapping' => [
  244. 'match' => '*',
  245. 'match_mapping_type' => 'string',
  246. 'mapping' => [
  247. 'type' => 'text',
  248. 'index' => false,
  249. 'copy_to' => '_search'
  250. ],
  251. ],
  252. ],
  253. [
  254. 'position_mapping' => [
  255. 'match' => 'position_*',
  256. 'match_mapping_type' => 'string',
  257. 'mapping' => [
  258. 'type' => 'int',
  259. ],
  260. ],
  261. ],
  262. ],
  263. ],
  264. ],
  265. ];
  266. foreach ($fields as $field => $fieldInfo) {
  267. $params['body'][$entityType]['properties'][$field] = $fieldInfo;
  268. }
  269. $this->getClient()->indices()->putMapping($params);
  270. }
  271. /**
  272. * Delete mapping in Elasticsearch index
  273. *
  274. * @param string $index
  275. * @param string $entityType
  276. * @return void
  277. */
  278. public function deleteMapping($index, $entityType)
  279. {
  280. $this->getClient()->indices()->deleteMapping([
  281. 'index' => $index,
  282. 'type' => $entityType,
  283. ]);
  284. }
  285. /**
  286. * Execute search by $query
  287. *
  288. * @param array $query
  289. * @return array
  290. */
  291. public function query($query)
  292. {
  293. return $this->getClient()->search($query);
  294. }
  295. /**
  296. * Execute suggest query
  297. *
  298. * @param array $query
  299. * @return array
  300. */
  301. public function suggest($query)
  302. {
  303. return $this->getClient()->suggest($query);
  304. }
  305. }