Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Elasticsearch.php
Go to the documentation of this file.
1 <?php
8 
10 
16 {
20  const BULK_ACTION_INDEX = 'index';
21  const BULK_ACTION_CREATE = 'create';
22  const BULK_ACTION_DELETE = 'delete';
23  const BULK_ACTION_UPDATE = 'update';
29  private const MAPPING_TOTAL_FIELDS_BUFFER_LIMIT = 1000;
30 
32  protected $connectionManager;
33 
39 
43  protected $indexNameResolver;
44 
48  protected $fieldMapper;
49 
53  protected $clientConfig;
54 
58  protected $client;
59 
63  protected $indexBuilder;
64 
68  protected $logger;
69 
73  protected $preparedIndex = [];
74 
78  private $batchDocumentDataMapper;
79 
94  public function __construct(
95  \Magento\Elasticsearch\SearchAdapter\ConnectionManager $connectionManager,
99  \Magento\Elasticsearch\Model\Adapter\Index\BuilderInterface $indexBuilder,
100  \Psr\Log\LoggerInterface $logger,
101  \Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver $indexNameResolver,
102  $options = [],
103  BatchDataMapperInterface $batchDocumentDataMapper = null
104  ) {
105  $this->connectionManager = $connectionManager;
106  $this->documentDataMapper = $documentDataMapper;
107  $this->fieldMapper = $fieldMapper;
108  $this->clientConfig = $clientConfig;
109  $this->indexBuilder = $indexBuilder;
110  $this->logger = $logger;
111  $this->indexNameResolver = $indexNameResolver;
112  $this->batchDocumentDataMapper = $batchDocumentDataMapper ?:
113  ObjectManager::getInstance()->get(BatchDataMapperInterface::class);
114 
115  try {
116  $this->client = $this->connectionManager->getConnection($options);
117  } catch (\Exception $e) {
118  $this->logger->critical($e);
119  throw new \Magento\Framework\Exception\LocalizedException(
120  __('The search failed because of a search engine misconfiguration.')
121  );
122  }
123  }
124 
131  public function ping()
132  {
133  try {
134  $response = $this->client->ping();
135  } catch (\Exception $e) {
136  throw new \Magento\Framework\Exception\LocalizedException(
137  __('Could not ping search engine: %1', $e->getMessage())
138  );
139  }
140  return $response;
141  }
142 
150  public function prepareDocsPerStore(array $documentData, $storeId)
151  {
152  $documents = [];
153  if (count($documentData)) {
154  $documents = $this->batchDocumentDataMapper->map(
155  $documentData,
156  $storeId
157  );
158  }
159  return $documents;
160  }
161 
171  public function addDocs(array $documents, $storeId, $mappedIndexerId)
172  {
173  if (count($documents)) {
174  try {
175  $indexName = $this->indexNameResolver->getIndexName($storeId, $mappedIndexerId, $this->preparedIndex);
176  $bulkIndexDocuments = $this->getDocsArrayInBulkIndexFormat($documents, $indexName);
177  $this->client->bulkQuery($bulkIndexDocuments);
178  } catch (\Exception $e) {
179  $this->logger->critical($e);
180  throw $e;
181  }
182  }
183 
184  return $this;
185  }
186 
194  public function cleanIndex($storeId, $mappedIndexerId)
195  {
196  $this->checkIndex($storeId, $mappedIndexerId, true);
197  $indexName = $this->indexNameResolver->getIndexName($storeId, $mappedIndexerId, $this->preparedIndex);
198  if ($this->client->isEmptyIndex($indexName)) {
199  // use existing index if empty
200  return $this;
201  }
202 
203  // prepare new index name and increase version
204  $indexPattern = $this->indexNameResolver->getIndexPattern($storeId, $mappedIndexerId);
205  $version = intval(str_replace($indexPattern, '', $indexName));
206  $newIndexName = $indexPattern . ++$version;
207 
208  // remove index if already exists
209  if ($this->client->indexExists($newIndexName)) {
210  $this->client->deleteIndex($newIndexName);
211  }
212 
213  // prepare new index
214  $this->prepareIndex($storeId, $newIndexName, $mappedIndexerId);
215 
216  return $this;
217  }
218 
228  public function deleteDocs(array $documentIds, $storeId, $mappedIndexerId)
229  {
230  try {
231  $this->checkIndex($storeId, $mappedIndexerId, false);
232  $indexName = $this->indexNameResolver->getIndexName($storeId, $mappedIndexerId, $this->preparedIndex);
233  $bulkDeleteDocuments = $this->getDocsArrayInBulkIndexFormat(
234  $documentIds,
235  $indexName,
236  self::BULK_ACTION_DELETE
237  );
238  $this->client->bulkQuery($bulkDeleteDocuments);
239  } catch (\Exception $e) {
240  $this->logger->critical($e);
241  throw $e;
242  }
243 
244  return $this;
245  }
246 
255  protected function getDocsArrayInBulkIndexFormat(
256  $documents,
257  $indexName,
258  $action = self::BULK_ACTION_INDEX
259  ) {
260  $bulkArray = [
261  'index' => $indexName,
262  'type' => $this->clientConfig->getEntityType(),
263  'body' => [],
264  'refresh' => true,
265  ];
266 
267  foreach ($documents as $id => $document) {
268  $bulkArray['body'][] = [
269  $action => [
270  '_id' => $id,
271  '_type' => $this->clientConfig->getEntityType(),
272  '_index' => $indexName
273  ]
274  ];
275  if ($action == self::BULK_ACTION_INDEX) {
276  $bulkArray['body'][] = $document;
277  }
278  }
279 
280  return $bulkArray;
281  }
282 
291  public function checkIndex(
292  $storeId,
293  $mappedIndexerId,
294  $checkAlias = true
295  ) {
296  // create new index for store
297  $indexName = $this->indexNameResolver->getIndexName($storeId, $mappedIndexerId, $this->preparedIndex);
298  if (!$this->client->indexExists($indexName)) {
299  $this->prepareIndex($storeId, $indexName, $mappedIndexerId);
300  }
301 
302  // add index to alias
303  if ($checkAlias) {
304  $namespace = $this->indexNameResolver->getIndexNameForAlias($storeId, $mappedIndexerId);
305  if (!$this->client->existsAlias($namespace, $indexName)) {
306  $this->client->updateAlias($namespace, $indexName);
307  }
308  }
309  return $this;
310  }
311 
319  public function updateAlias($storeId, $mappedIndexerId)
320  {
321  if (!isset($this->preparedIndex[$storeId])) {
322  return $this;
323  }
324 
325  $oldIndex = $this->indexNameResolver->getIndexFromAlias($storeId, $mappedIndexerId);
326  if ($oldIndex == $this->preparedIndex[$storeId]) {
327  $oldIndex = '';
328  }
329 
330  $this->client->updateAlias(
331  $this->indexNameResolver->getIndexNameForAlias($storeId, $mappedIndexerId),
332  $this->preparedIndex[$storeId],
333  $oldIndex
334  );
335 
336  // remove obsolete index
337  if ($oldIndex) {
338  $this->client->deleteIndex($oldIndex);
339  }
340 
341  return $this;
342  }
343 
352  protected function prepareIndex($storeId, $indexName, $mappedIndexerId)
353  {
354  $this->indexBuilder->setStoreId($storeId);
355  $settings = $this->indexBuilder->build();
356  $allAttributeTypes = $this->fieldMapper->getAllAttributesTypes([
357  'entityType' => $mappedIndexerId,
358  // Use store id instead of website id from context for save existing fields mapping.
359  // In future websiteId will be eliminated due to index stored per store
360  'websiteId' => $storeId
361  ]);
362  $settings['index']['mapping']['total_fields']['limit'] = $this->getMappingTotalFieldsLimit($allAttributeTypes);
363  $this->client->createIndex($indexName, ['settings' => $settings]);
364  $this->client->addFieldsMapping(
365  $allAttributeTypes,
366  $indexName,
367  $this->clientConfig->getEntityType()
368  );
369  $this->preparedIndex[$storeId] = $indexName;
370  return $this;
371  }
372 
379  private function getMappingTotalFieldsLimit(array $allAttributeTypes): int
380  {
381  return count($allAttributeTypes) + self::MAPPING_TOTAL_FIELDS_BUFFER_LIMIT;
382  }
383 }
$response
Definition: 404.php:11
deleteDocs(array $documentIds, $storeId, $mappedIndexerId)
prepareDocsPerStore(array $documentData, $storeId)
$id
Definition: fieldset.phtml:14
__()
Definition: __.php:13
addDocs(array $documents, $storeId, $mappedIndexerId)
checkIndex( $storeId, $mappedIndexerId, $checkAlias=true)
$settings
Definition: bootstrap.php:29
prepareIndex($storeId, $indexName, $mappedIndexerId)
getDocsArrayInBulkIndexFormat( $documents, $indexName, $action=self::BULK_ACTION_INDEX)
__construct(\Magento\Elasticsearch\SearchAdapter\ConnectionManager $connectionManager, DataMapperInterface $documentDataMapper, FieldMapperInterface $fieldMapper, \Magento\Elasticsearch\Model\Config $clientConfig, \Magento\Elasticsearch\Model\Adapter\Index\BuilderInterface $indexBuilder, \Psr\Log\LoggerInterface $logger, \Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver $indexNameResolver, $options=[], BatchDataMapperInterface $batchDocumentDataMapper=null)