vendor/ruflin/elastica/src/Index.php line 472

Open in your IDE?
  1. <?php
  2. namespace Elastica;
  3. use Elastica\Bulk\ResponseSet;
  4. use Elastica\Exception\InvalidException;
  5. use Elastica\Exception\NotFoundException;
  6. use Elastica\Exception\ResponseException;
  7. use Elastica\Index\Recovery as IndexRecovery;
  8. use Elastica\Index\Settings as IndexSettings;
  9. use Elastica\Index\Stats as IndexStats;
  10. use Elastica\Query\AbstractQuery;
  11. use Elastica\ResultSet\BuilderInterface;
  12. use Elastica\Script\AbstractScript;
  13. use Elasticsearch\Endpoints\AbstractEndpoint;
  14. use Elasticsearch\Endpoints\DeleteByQuery;
  15. use Elasticsearch\Endpoints\Get as DocumentGet;
  16. use Elasticsearch\Endpoints\Index as IndexEndpoint;
  17. use Elasticsearch\Endpoints\Indices\Alias;
  18. use Elasticsearch\Endpoints\Indices\Aliases\Update;
  19. use Elasticsearch\Endpoints\Indices\Analyze;
  20. use Elasticsearch\Endpoints\Indices\Cache\Clear;
  21. use Elasticsearch\Endpoints\Indices\ClearCache;
  22. use Elasticsearch\Endpoints\Indices\Close;
  23. use Elasticsearch\Endpoints\Indices\Create;
  24. use Elasticsearch\Endpoints\Indices\Delete;
  25. use Elasticsearch\Endpoints\Indices\DeleteAlias;
  26. use Elasticsearch\Endpoints\Indices\Exists;
  27. use Elasticsearch\Endpoints\Indices\Flush;
  28. use Elasticsearch\Endpoints\Indices\ForceMerge;
  29. use Elasticsearch\Endpoints\Indices\GetAlias;
  30. use Elasticsearch\Endpoints\Indices\GetMapping;
  31. use Elasticsearch\Endpoints\Indices\Mapping\Get as MappingGet;
  32. use Elasticsearch\Endpoints\Indices\Open;
  33. use Elasticsearch\Endpoints\Indices\PutSettings;
  34. use Elasticsearch\Endpoints\Indices\Refresh;
  35. use Elasticsearch\Endpoints\Indices\Settings\Put;
  36. use Elasticsearch\Endpoints\Indices\UpdateAliases;
  37. use Elasticsearch\Endpoints\OpenPointInTime;
  38. use Elasticsearch\Endpoints\UpdateByQuery;
  39. /**
  40.  * Elastica index object.
  41.  *
  42.  * Handles reads, deletes and configurations of an index
  43.  *
  44.  * @author   Nicolas Ruflin <spam@ruflin.com>
  45.  */
  46. class Index implements SearchableInterface
  47. {
  48.     /**
  49.      * Index name.
  50.      *
  51.      * @var string Index name
  52.      */
  53.     protected $_name;
  54.     /**
  55.      * Client object.
  56.      *
  57.      * @var Client Client object
  58.      */
  59.     protected $_client;
  60.     /**
  61.      * Creates a new index object.
  62.      *
  63.      * All the communication to and from an index goes of this object
  64.      *
  65.      * @param Client $client Client object
  66.      * @param string $name   Index name
  67.      */
  68.     public function __construct(Client $clientstring $name)
  69.     {
  70.         $this->_client $client;
  71.         $this->_name $name;
  72.     }
  73.     /**
  74.      * Return Index Stats.
  75.      *
  76.      * @return IndexStats
  77.      */
  78.     public function getStats()
  79.     {
  80.         return new IndexStats($this);
  81.     }
  82.     /**
  83.      * Return Index Recovery.
  84.      *
  85.      * @return IndexRecovery
  86.      */
  87.     public function getRecovery()
  88.     {
  89.         return new IndexRecovery($this);
  90.     }
  91.     /**
  92.      * Sets the mappings for the current index.
  93.      *
  94.      * @param Mapping $mapping MappingType object
  95.      * @param array   $query   querystring when put mapping (for example update_all_types)
  96.      */
  97.     public function setMapping(Mapping $mapping, array $query = []): Response
  98.     {
  99.         return $mapping->send($this$query);
  100.     }
  101.     /**
  102.      * Gets all mappings for the current index.
  103.      */
  104.     public function getMapping(): array
  105.     {
  106.         // TODO: Use only GetMapping when dropping support for elasticsearch/elasticsearch 7.x
  107.         $endpoint \class_exists(GetMapping::class) ? new GetMapping() : new MappingGet();
  108.         $response $this->requestEndpoint($endpoint);
  109.         $data $response->getData();
  110.         // Get first entry as if index is an Alias, the name of the mapping is the real name and not alias name
  111.         $mapping \array_shift($data);
  112.         return $mapping['mappings'] ?? [];
  113.     }
  114.     /**
  115.      * Returns the index settings object.
  116.      *
  117.      * @return IndexSettings
  118.      */
  119.     public function getSettings()
  120.     {
  121.         return new IndexSettings($this);
  122.     }
  123.     /**
  124.      * @param array|string $data
  125.      *
  126.      * @return Document
  127.      */
  128.     public function createDocument(string $id ''$data = [])
  129.     {
  130.         return new Document($id$data$this);
  131.     }
  132.     /**
  133.      * Uses _bulk to send documents to the server.
  134.      *
  135.      * @param Document[] $docs    Array of Elastica\Document
  136.      * @param array      $options Array of query params to use for query. For possible options check es api
  137.      *
  138.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
  139.      */
  140.     public function updateDocuments(array $docs, array $options = []): ResponseSet
  141.     {
  142.         foreach ($docs as $doc) {
  143.             $doc->setIndex($this->getName());
  144.         }
  145.         return $this->getClient()->updateDocuments($docs$options);
  146.     }
  147.     /**
  148.      * Update entries in the db based on a query.
  149.      *
  150.      * @param AbstractQuery|array|Collapse|Query|string|Suggest $query   Query object or array
  151.      * @param AbstractScript                                    $script  Script
  152.      * @param array                                             $options Optional params
  153.      *
  154.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update-by-query.html
  155.      */
  156.     public function updateByQuery($queryAbstractScript $script, array $options = []): Response
  157.     {
  158.         $endpoint = new UpdateByQuery();
  159.         $q Query::create($query)->getQuery();
  160.         $body = [
  161.             'query' => \is_array($q) ? $q $q->toArray(),
  162.             'script' => $script->toArray()['script'],
  163.         ];
  164.         $endpoint->setBody($body);
  165.         $endpoint->setParams($options);
  166.         return $this->requestEndpoint($endpoint);
  167.     }
  168.     /**
  169.      * Adds the given document to the search index.
  170.      */
  171.     public function addDocument(Document $doc): Response
  172.     {
  173.         $endpoint = new IndexEndpoint();
  174.         if (null !== $doc->getId() && '' !== $doc->getId()) {
  175.             $endpoint->setId($doc->getId());
  176.         }
  177.         $options $doc->getOptions(
  178.             [
  179.                 'consistency',
  180.                 'op_type',
  181.                 'parent',
  182.                 'percolate',
  183.                 'pipeline',
  184.                 'refresh',
  185.                 'replication',
  186.                 'retry_on_conflict',
  187.                 'routing',
  188.                 'timeout',
  189.             ]
  190.         );
  191.         $endpoint->setBody($doc->getData());
  192.         $endpoint->setParams($options);
  193.         $response $this->requestEndpoint($endpoint);
  194.         $data $response->getData();
  195.         // set autogenerated id to document
  196.         if ($response->isOk() && (
  197.             $doc->isAutoPopulate() || $this->getClient()->getConfigValue(['document''autoPopulate'], false)
  198.         )) {
  199.             if (isset($data['_id']) && !$doc->hasId()) {
  200.                 $doc->setId($data['_id']);
  201.             }
  202.             $doc->setVersionParams($data);
  203.         }
  204.         return $response;
  205.     }
  206.     /**
  207.      * Uses _bulk to send documents to the server.
  208.      *
  209.      * @param array|Document[] $docs    Array of Elastica\Document
  210.      * @param array            $options Array of query params to use for query. For possible options check es api
  211.      *
  212.      * @return ResponseSet
  213.      *
  214.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
  215.      */
  216.     public function addDocuments(array $docs, array $options = [])
  217.     {
  218.         foreach ($docs as $doc) {
  219.             $doc->setIndex($this->getName());
  220.         }
  221.         return $this->getClient()->addDocuments($docs$options);
  222.     }
  223.     /**
  224.      * Get the document from search index.
  225.      *
  226.      * @param int|string $id      Document id
  227.      * @param array      $options options for the get request
  228.      *
  229.      * @throws ResponseException
  230.      * @throws NotFoundException
  231.      */
  232.     public function getDocument($id, array $options = []): Document
  233.     {
  234.         $endpoint = new DocumentGet();
  235.         $endpoint->setId($id);
  236.         $endpoint->setParams($options);
  237.         $response $this->requestEndpoint($endpoint);
  238.         $result $response->getData();
  239.         if (!isset($result['found']) || false === $result['found']) {
  240.             throw new NotFoundException('doc id '.$id.' not found');
  241.         }
  242.         if (isset($result['fields'])) {
  243.             $data $result['fields'];
  244.         } elseif (isset($result['_source'])) {
  245.             $data $result['_source'];
  246.         } else {
  247.             $data = [];
  248.         }
  249.         $doc = new Document($id$data$this->getName());
  250.         $doc->setVersionParams($result);
  251.         return $doc;
  252.     }
  253.     /**
  254.      * Deletes a document by its unique identifier.
  255.      *
  256.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete.html
  257.      */
  258.     public function deleteById(string $id, array $options = []): Response
  259.     {
  260.         if (!\trim($id)) {
  261.             throw new NotFoundException('Doc id "'.$id.'" not found and can not be deleted');
  262.         }
  263.         $endpoint = new \Elasticsearch\Endpoints\Delete();
  264.         $endpoint->setId(\trim($id));
  265.         $endpoint->setParams($options);
  266.         return $this->requestEndpoint($endpoint);
  267.     }
  268.     /**
  269.      * Deletes documents matching the given query.
  270.      *
  271.      * @param AbstractQuery|array|Collapse|Query|string|Suggest $query   Query object or array
  272.      * @param array                                             $options Optional params
  273.      *
  274.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html
  275.      */
  276.     public function deleteByQuery($query, array $options = []): Response
  277.     {
  278.         $query Query::create($query)->getQuery();
  279.         $endpoint = new DeleteByQuery();
  280.         $endpoint->setBody(['query' => \is_array($query) ? $query $query->toArray()]);
  281.         $endpoint->setParams($options);
  282.         return $this->requestEndpoint($endpoint);
  283.     }
  284.     /**
  285.      * Opens a Point-in-Time on the index.
  286.      *
  287.      * @see: https://www.elastic.co/guide/en/elasticsearch/reference/current/point-in-time-api.html
  288.      */
  289.     public function openPointInTime(string $keepAlive): Response
  290.     {
  291.         $endpoint = new OpenPointInTime();
  292.         $endpoint->setParams(['keep_alive' => $keepAlive]);
  293.         return $this->requestEndpoint($endpoint);
  294.     }
  295.     /**
  296.      * Deletes the index.
  297.      */
  298.     public function delete(): Response
  299.     {
  300.         return $this->requestEndpoint(new Delete());
  301.     }
  302.     /**
  303.      * Uses the "_bulk" endpoint to delete documents from the server.
  304.      *
  305.      * @param Document[] $docs Array of documents
  306.      *
  307.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
  308.      */
  309.     public function deleteDocuments(array $docs): ResponseSet
  310.     {
  311.         foreach ($docs as $doc) {
  312.             $doc->setIndex($this->getName());
  313.         }
  314.         return $this->getClient()->deleteDocuments($docs);
  315.     }
  316.     /**
  317.      * Force merges index.
  318.      *
  319.      * Detailed arguments can be found here in the ES documentation.
  320.      *
  321.      * @param array $args Additional arguments
  322.      *
  323.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-forcemerge.html
  324.      */
  325.     public function forcemerge($args = []): Response
  326.     {
  327.         $endpoint = new ForceMerge();
  328.         $endpoint->setParams($args);
  329.         return $this->requestEndpoint($endpoint);
  330.     }
  331.     /**
  332.      * Refreshes the index.
  333.      *
  334.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-refresh.html
  335.      */
  336.     public function refresh(): Response
  337.     {
  338.         return $this->requestEndpoint(new Refresh());
  339.     }
  340.     /**
  341.      * Creates a new index with the given arguments.
  342.      *
  343.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html
  344.      *
  345.      * @param array      $args    Additional arguments to pass to the Create endpoint
  346.      * @param array|bool $options OPTIONAL
  347.      *                            bool=> Deletes index first if already exists (default = false).
  348.      *                            array => Associative array of options (option=>value)
  349.      *
  350.      * @throws InvalidException
  351.      * @throws ResponseException
  352.      *
  353.      * @return Response Server response
  354.      */
  355.     public function create(array $args = [], $options null): Response
  356.     {
  357.         if (null === $options) {
  358.             if (\func_num_args() >= 2) {
  359.                 \trigger_deprecation('ruflin/elastica''7.1.0''Passing null as 2nd argument to "%s()" is deprecated, avoid passing this argument or pass an array instead. It will be removed in 8.0.'__METHOD__);
  360.             }
  361.             $options = [];
  362.         } elseif (\is_bool($options)) {
  363.             \trigger_deprecation('ruflin/elastica''7.1.0''Passing a bool as 2nd argument to "%s()" is deprecated, pass an array with the key "recreate" instead. It will be removed in 8.0.'__METHOD__);
  364.             $options = ['recreate' => $options];
  365.         } elseif (!\is_array($options)) {
  366.             throw new \TypeError(\sprintf('Argument 2 passed to "%s()" must be of type array|bool|null, %s given.'__METHOD__\is_object($options) ? \get_class($options) : \gettype($options)));
  367.         }
  368.         $endpoint = new Create();
  369.         $invalidOptions \array_diff(\array_keys($options), $allowedOptions \array_merge($endpoint->getParamWhitelist(), [
  370.             'recreate',
  371.         ]));
  372.         if (=== $invalidOptionCount \count($invalidOptions)) {
  373.             throw new InvalidException(\sprintf('"%s" is not a valid option. Allowed options are "%s".'\implode('", "'$invalidOptions), \implode('", "'$allowedOptions)));
  374.         }
  375.         if ($invalidOptionCount 1) {
  376.             throw new InvalidException(\sprintf('"%s" are not valid options. Allowed options are "%s".'\implode('", "'$invalidOptions), \implode('", "'$allowedOptions)));
  377.         }
  378.         if ($options['recreate'] ?? false) {
  379.             try {
  380.                 $this->delete();
  381.             } catch (ResponseException $e) {
  382.                 // Index can't be deleted, because it doesn't exist
  383.             }
  384.         }
  385.         unset($options['recreate']);
  386.         $endpoint->setParams($options);
  387.         $endpoint->setBody($args);
  388.         return $this->requestEndpoint($endpoint);
  389.     }
  390.     /**
  391.      * Checks if the given index exists ans is created.
  392.      */
  393.     public function exists(): bool
  394.     {
  395.         $response $this->requestEndpoint(new Exists());
  396.         return 200 === $response->getStatus();
  397.     }
  398.     /**
  399.      * {@inheritdoc}
  400.      */
  401.     public function createSearch($query ''$options null, ?BuilderInterface $builder null): Search
  402.     {
  403.         $search = new Search($this->getClient(), $builder);
  404.         $search->addIndex($this);
  405.         $search->setOptionsAndQuery($options$query);
  406.         return $search;
  407.     }
  408.     /**
  409.      * {@inheritdoc}
  410.      */
  411.     public function search($query ''$options nullstring $method Request::POST): ResultSet
  412.     {
  413.         $search $this->createSearch($query$options);
  414.         return $search->search(''null$method);
  415.     }
  416.     /**
  417.      * {@inheritdoc}
  418.      */
  419.     public function count($query ''string $method Request::POST): int
  420.     {
  421.         $search $this->createSearch($query);
  422.         return $search->count(''false$method);
  423.     }
  424.     /**
  425.      * Opens an index.
  426.      *
  427.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-open-close.html
  428.      */
  429.     public function open(): Response
  430.     {
  431.         return $this->requestEndpoint(new Open());
  432.     }
  433.     /**
  434.      * Closes the index.
  435.      *
  436.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-open-close.html
  437.      */
  438.     public function close(): Response
  439.     {
  440.         return $this->requestEndpoint(new Close());
  441.     }
  442.     /**
  443.      * Returns the index name.
  444.      */
  445.     public function getName(): string
  446.     {
  447.         return $this->_name;
  448.     }
  449.     /**
  450.      * Returns index client.
  451.      */
  452.     public function getClient(): Client
  453.     {
  454.         return $this->_client;
  455.     }
  456.     /**
  457.      * Adds an alias to the current index.
  458.      *
  459.      * @param bool $replace If set, an existing alias will be replaced
  460.      *
  461.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html
  462.      */
  463.     public function addAlias(string $namebool $replace false): Response
  464.     {
  465.         $data = ['actions' => []];
  466.         if ($replace) {
  467.             $status = new Status($this->getClient());
  468.             foreach ($status->getIndicesWithAlias($name) as $index) {
  469.                 $data['actions'][] = ['remove' => ['index' => $index->getName(), 'alias' => $name]];
  470.             }
  471.         }
  472.         $data['actions'][] = ['add' => ['index' => $this->getName(), 'alias' => $name]];
  473.         // TODO: Use only UpdateAliases when dropping support for elasticsearch/elasticsearch 7.x
  474.         $endpoint \class_exists(UpdateAliases::class) ? new UpdateAliases() : new Update();
  475.         $endpoint->setBody($data);
  476.         return $this->getClient()->requestEndpoint($endpoint);
  477.     }
  478.     /**
  479.      * Removes an alias pointing to the current index.
  480.      *
  481.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html
  482.      */
  483.     public function removeAlias(string $name): Response
  484.     {
  485.         // TODO: Use only DeleteAlias when dropping support for elasticsearch/elasticsearch 7.x
  486.         $endpoint \class_exists(DeleteAlias::class) ? new DeleteAlias() : new Alias\Delete();
  487.         $endpoint->setName($name);
  488.         return $this->requestEndpoint($endpoint);
  489.     }
  490.     /**
  491.      * Returns all index aliases.
  492.      *
  493.      * @return string[]
  494.      */
  495.     public function getAliases(): array
  496.     {
  497.         // TODO: Use only GetAlias when dropping support for elasticsearch/elasticsearch 7.x
  498.         $endpoint \class_exists(GetAlias::class) ? new GetAlias() : new Alias\Get();
  499.         $endpoint->setName('*');
  500.         $responseData $this->requestEndpoint($endpoint)->getData();
  501.         if (!isset($responseData[$this->getName()])) {
  502.             return [];
  503.         }
  504.         $data $responseData[$this->getName()];
  505.         if (!empty($data['aliases'])) {
  506.             return \array_keys($data['aliases']);
  507.         }
  508.         return [];
  509.     }
  510.     /**
  511.      * Checks if the index has the given alias.
  512.      */
  513.     public function hasAlias(string $name): bool
  514.     {
  515.         return \in_array($name$this->getAliases(), true);
  516.     }
  517.     /**
  518.      * Clears the cache of an index.
  519.      *
  520.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-clearcache.html
  521.      */
  522.     public function clearCache(): Response
  523.     {
  524.         // TODO: Use only ClearCache when dropping support for elasticsearch/elasticsearch 7.x
  525.         $endpoint \class_exists(ClearCache::class) ? new ClearCache() : new Clear();
  526.         // TODO: add additional cache clean arguments
  527.         return $this->requestEndpoint($endpoint);
  528.     }
  529.     /**
  530.      * Flushes the index to storage.
  531.      *
  532.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-flush.html
  533.      */
  534.     public function flush(array $options = []): Response
  535.     {
  536.         $endpoint = new Flush();
  537.         $endpoint->setParams($options);
  538.         return $this->requestEndpoint($endpoint);
  539.     }
  540.     /**
  541.      * Can be used to change settings during runtime. One example is to use it for bulk updating.
  542.      *
  543.      * @param array $data Data array
  544.      *
  545.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html
  546.      */
  547.     public function setSettings(array $data): Response
  548.     {
  549.         // TODO: Use only PutSettings when dropping support for elasticsearch/elasticsearch 7.x
  550.         $endpoint \class_exists(PutSettings::class) ? new PutSettings() : new Put();
  551.         $endpoint->setBody($data);
  552.         return $this->requestEndpoint($endpoint);
  553.     }
  554.     /**
  555.      * Makes calls to the elasticsearch server based on this index.
  556.      *
  557.      * @param string       $path   Path to call
  558.      * @param string       $method Rest method to use (GET, POST, DELETE, PUT)
  559.      * @param array|string $data   Arguments as array or encoded string
  560.      */
  561.     public function request(string $pathstring $method$data = [], array $queryParameters = []): Response
  562.     {
  563.         $path $this->getName().'/'.$path;
  564.         return $this->getClient()->request($path$method$data$queryParameters);
  565.     }
  566.     /**
  567.      * Makes calls to the elasticsearch server with usage official client Endpoint based on this index.
  568.      */
  569.     public function requestEndpoint(AbstractEndpoint $endpoint): Response
  570.     {
  571.         $cloned = clone $endpoint;
  572.         $cloned->setIndex($this->getName());
  573.         return $this->getClient()->requestEndpoint($cloned);
  574.     }
  575.     /**
  576.      * Run the analysis on the index.
  577.      *
  578.      * @param array $body request body for the `_analyze` API, see API documentation for the required properties
  579.      * @param array $args Additional arguments
  580.      *
  581.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-analyze.html
  582.      */
  583.     public function analyze(array $body$args = []): array
  584.     {
  585.         $endpoint = new Analyze();
  586.         $endpoint->setBody($body);
  587.         $endpoint->setParams($args);
  588.         $data $this->requestEndpoint($endpoint)->getData();
  589.         // Support for "Explain" parameter, that returns a different response structure from Elastic
  590.         // @see: https://www.elastic.co/guide/en/elasticsearch/reference/current/_explain_analyze.html
  591.         if (isset($body['explain']) && $body['explain']) {
  592.             return $data['detail'];
  593.         }
  594.         return $data['tokens'];
  595.     }
  596.     /**
  597.      * Update document, using update script.
  598.      *
  599.      * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html
  600.      *
  601.      * @param AbstractScript|Document $data    Document or Script with update data
  602.      * @param array                   $options array of query params to use for query
  603.      */
  604.     public function updateDocument($data, array $options = []): Response
  605.     {
  606.         if (!($data instanceof Document) && !($data instanceof AbstractScript)) {
  607.             throw new \InvalidArgumentException('Data should be a Document or Script');
  608.         }
  609.         if (!$data->hasId()) {
  610.             throw new InvalidException('Document or Script id is not set');
  611.         }
  612.         return $this->getClient()->updateDocument($data->getId(), $data$this->getName(), $options);
  613.     }
  614. }