Index.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. <?php
  2. namespace Elastica;
  3. use Elastica\Exception\InvalidException;
  4. use Elastica\Exception\ResponseException;
  5. use Elastica\Index\Recovery as IndexRecovery;
  6. use Elastica\Index\Settings as IndexSettings;
  7. use Elastica\Index\Stats as IndexStats;
  8. use Elastica\ResultSet\BuilderInterface;
  9. use Elastica\Script\AbstractScript;
  10. use Elasticsearch\Endpoints\AbstractEndpoint;
  11. use Elasticsearch\Endpoints\DeleteByQuery;
  12. use Elasticsearch\Endpoints\Indices\Aliases\Update;
  13. use Elasticsearch\Endpoints\Indices\Analyze;
  14. use Elasticsearch\Endpoints\Indices\Cache\Clear;
  15. use Elasticsearch\Endpoints\Indices\Close;
  16. use Elasticsearch\Endpoints\Indices\Create;
  17. use Elasticsearch\Endpoints\Indices\Delete;
  18. use Elasticsearch\Endpoints\Indices\Exists;
  19. use Elasticsearch\Endpoints\Indices\Flush;
  20. use Elasticsearch\Endpoints\Indices\ForceMerge;
  21. use Elasticsearch\Endpoints\Indices\Mapping\Get;
  22. use Elasticsearch\Endpoints\Indices\Open;
  23. use Elasticsearch\Endpoints\Indices\Refresh;
  24. use Elasticsearch\Endpoints\Indices\Settings\Put;
  25. use Elasticsearch\Endpoints\UpdateByQuery;
  26. /**
  27. * Elastica index object.
  28. *
  29. * Handles reads, deletes and configurations of an index
  30. *
  31. * @author Nicolas Ruflin <spam@ruflin.com>
  32. */
  33. class Index implements SearchableInterface
  34. {
  35. /**
  36. * Index name.
  37. *
  38. * @var string Index name
  39. */
  40. protected $_name;
  41. /**
  42. * Client object.
  43. *
  44. * @var \Elastica\Client Client object
  45. */
  46. protected $_client;
  47. /**
  48. * Creates a new index object.
  49. *
  50. * All the communication to and from an index goes of this object
  51. *
  52. * @param \Elastica\Client $client Client object
  53. * @param string $name Index name
  54. */
  55. public function __construct(Client $client, $name)
  56. {
  57. $this->_client = $client;
  58. if (!is_scalar($name)) {
  59. throw new InvalidException('Index name should be a scalar type');
  60. }
  61. $this->_name = (string) $name;
  62. }
  63. /**
  64. * Returns a type object for the current index with the given name.
  65. *
  66. * @param string $type Type name
  67. *
  68. * @return \Elastica\Type Type object
  69. */
  70. public function getType($type)
  71. {
  72. return new Type($this, $type);
  73. }
  74. /**
  75. * Return Index Stats.
  76. *
  77. * @return \Elastica\Index\Stats
  78. */
  79. public function getStats()
  80. {
  81. return new IndexStats($this);
  82. }
  83. /**
  84. * Return Index Recovery.
  85. *
  86. * @return \Elastica\Index\Recovery
  87. */
  88. public function getRecovery()
  89. {
  90. return new IndexRecovery($this);
  91. }
  92. /**
  93. * Gets all the type mappings for an index.
  94. *
  95. * @return array
  96. */
  97. public function getMapping()
  98. {
  99. $response = $this->requestEndpoint(new Get());
  100. $data = $response->getData();
  101. // Get first entry as if index is an Alias, the name of the mapping is the real name and not alias name
  102. $mapping = array_shift($data);
  103. if (isset($mapping['mappings'])) {
  104. return $mapping['mappings'];
  105. }
  106. return [];
  107. }
  108. /**
  109. * Returns the index settings object.
  110. *
  111. * @return \Elastica\Index\Settings Settings object
  112. */
  113. public function getSettings()
  114. {
  115. return new IndexSettings($this);
  116. }
  117. /**
  118. * Uses _bulk to send documents to the server.
  119. *
  120. * @param array|\Elastica\Document[] $docs Array of Elastica\Document
  121. * @param array $options Array of query params to use for query. For possible options check es api
  122. *
  123. * @return \Elastica\Bulk\ResponseSet
  124. *
  125. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
  126. */
  127. public function updateDocuments(array $docs, array $options = [])
  128. {
  129. foreach ($docs as $doc) {
  130. $doc->setIndex($this->getName());
  131. }
  132. return $this->getClient()->updateDocuments($docs, $options);
  133. }
  134. /**
  135. * Update entries in the db based on a query.
  136. *
  137. * @param \Elastica\Query|string|array $query Query object or array
  138. * @param AbstractScript $script Script
  139. * @param array $options Optional params
  140. *
  141. * @return \Elastica\Response
  142. *
  143. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update-by-query.html
  144. */
  145. public function updateByQuery($query, AbstractScript $script, array $options = [])
  146. {
  147. $query = Query::create($query)->getQuery();
  148. $endpoint = new UpdateByQuery();
  149. $body = ['query' => is_array($query)
  150. ? $query
  151. : $query->toArray(), ];
  152. $body['script'] = $script->toArray()['script'];
  153. $endpoint->setBody($body);
  154. $endpoint->setParams($options);
  155. return $this->requestEndpoint($endpoint);
  156. }
  157. /**
  158. * Uses _bulk to send documents to the server.
  159. *
  160. * @param array|\Elastica\Document[] $docs Array of Elastica\Document
  161. * @param array $options Array of query params to use for query. For possible options check es api
  162. *
  163. * @return \Elastica\Bulk\ResponseSet
  164. *
  165. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
  166. */
  167. public function addDocuments(array $docs, array $options = [])
  168. {
  169. foreach ($docs as $doc) {
  170. $doc->setIndex($this->getName());
  171. }
  172. return $this->getClient()->addDocuments($docs, $options);
  173. }
  174. /**
  175. * Deletes entries in the db based on a query.
  176. *
  177. * @param \Elastica\Query|\Elastica\Query\AbstractQuery|string|array $query Query object or array
  178. * @param array $options Optional params
  179. *
  180. * @return \Elastica\Response
  181. *
  182. * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.0/docs-delete-by-query.html
  183. */
  184. public function deleteByQuery($query, array $options = [])
  185. {
  186. $query = Query::create($query)->getQuery();
  187. $endpoint = new DeleteByQuery();
  188. $endpoint->setBody(['query' => is_array($query) ? $query : $query->toArray()]);
  189. $endpoint->setParams($options);
  190. return $this->requestEndpoint($endpoint);
  191. }
  192. /**
  193. * Deletes the index.
  194. *
  195. * @return \Elastica\Response Response object
  196. */
  197. public function delete()
  198. {
  199. return $this->requestEndpoint(new Delete());
  200. }
  201. /**
  202. * Uses _bulk to delete documents from the server.
  203. *
  204. * @param array|\Elastica\Document[] $docs Array of Elastica\Document
  205. *
  206. * @return \Elastica\Bulk\ResponseSet
  207. *
  208. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
  209. */
  210. public function deleteDocuments(array $docs)
  211. {
  212. foreach ($docs as $doc) {
  213. $doc->setIndex($this->getName());
  214. }
  215. return $this->getClient()->deleteDocuments($docs);
  216. }
  217. /**
  218. * Force merges index.
  219. *
  220. * Detailed arguments can be found here in the link
  221. *
  222. * @param array $args OPTIONAL Additional arguments
  223. *
  224. * @return Response
  225. *
  226. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-forcemerge.html
  227. */
  228. public function forcemerge($args = [])
  229. {
  230. $endpoint = new ForceMerge();
  231. $endpoint->setParams($args);
  232. return $this->requestEndpoint($endpoint);
  233. }
  234. /**
  235. * Refreshes the index.
  236. *
  237. * @return \Elastica\Response Response object
  238. *
  239. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-refresh.html
  240. */
  241. public function refresh()
  242. {
  243. return $this->requestEndpoint(new Refresh());
  244. }
  245. /**
  246. * Creates a new index with the given arguments.
  247. *
  248. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html
  249. *
  250. * @param array $args OPTIONAL Arguments to use
  251. * @param bool|array $options OPTIONAL
  252. * bool=> Deletes index first if already exists (default = false).
  253. * array => Associative array of options (option=>value)
  254. *
  255. * @throws \Elastica\Exception\InvalidException
  256. * @throws \Elastica\Exception\ResponseException
  257. *
  258. * @return \Elastica\Response Server response
  259. */
  260. public function create(array $args = [], $options = null)
  261. {
  262. if (is_bool($options) && $options) {
  263. try {
  264. $this->delete();
  265. } catch (ResponseException $e) {
  266. // Table can't be deleted, because doesn't exist
  267. }
  268. } elseif (is_array($options)) {
  269. foreach ($options as $key => $value) {
  270. switch ($key) {
  271. case 'recreate':
  272. try {
  273. $this->delete();
  274. } catch (ResponseException $e) {
  275. // Table can't be deleted, because doesn't exist
  276. }
  277. break;
  278. default:
  279. throw new InvalidException('Invalid option '.$key);
  280. break;
  281. }
  282. }
  283. }
  284. $endpoint = new Create();
  285. $endpoint->setBody($args);
  286. return $this->requestEndpoint($endpoint);
  287. }
  288. /**
  289. * Checks if the given index is already created.
  290. *
  291. * @return bool True if index exists
  292. */
  293. public function exists()
  294. {
  295. $response = $this->requestEndpoint(new Exists());
  296. return 200 === $response->getStatus();
  297. }
  298. /**
  299. * @param string|array|\Elastica\Query $query
  300. * @param int|array $options
  301. * @param BuilderInterface $builder
  302. *
  303. * @return Search
  304. */
  305. public function createSearch($query = '', $options = null, BuilderInterface $builder = null)
  306. {
  307. $search = new Search($this->getClient(), $builder);
  308. $search->addIndex($this);
  309. $search->setOptionsAndQuery($options, $query);
  310. return $search;
  311. }
  312. /**
  313. * Searches in this index.
  314. *
  315. * @param string|array|\Elastica\Query $query Array with all query data inside or a Elastica\Query object
  316. * @param int|array $options OPTIONAL Limit or associative array of options (option=>value)
  317. *
  318. * @return \Elastica\ResultSet with all results inside
  319. *
  320. * @see \Elastica\SearchableInterface::search
  321. */
  322. public function search($query = '', $options = null)
  323. {
  324. $search = $this->createSearch($query, $options);
  325. return $search->search();
  326. }
  327. /**
  328. * Counts results of query.
  329. *
  330. * @param string|array|\Elastica\Query $query Array with all query data inside or a Elastica\Query object
  331. *
  332. * @return int number of documents matching the query
  333. *
  334. * @see \Elastica\SearchableInterface::count
  335. */
  336. public function count($query = '')
  337. {
  338. $search = $this->createSearch($query);
  339. return $search->count();
  340. }
  341. /**
  342. * Opens an index.
  343. *
  344. * @return \Elastica\Response Response object
  345. *
  346. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-open-close.html
  347. */
  348. public function open()
  349. {
  350. return $this->requestEndpoint(new Open());
  351. }
  352. /**
  353. * Closes the index.
  354. *
  355. * @return \Elastica\Response Response object
  356. *
  357. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-open-close.html
  358. */
  359. public function close()
  360. {
  361. return $this->requestEndpoint(new Close());
  362. }
  363. /**
  364. * Returns the index name.
  365. *
  366. * @return string Index name
  367. */
  368. public function getName()
  369. {
  370. return $this->_name;
  371. }
  372. /**
  373. * Returns index client.
  374. *
  375. * @return \Elastica\Client Index client object
  376. */
  377. public function getClient()
  378. {
  379. return $this->_client;
  380. }
  381. /**
  382. * Adds an alias to the current index.
  383. *
  384. * @param string $name Alias name
  385. * @param bool $replace OPTIONAL If set, an existing alias will be replaced
  386. *
  387. * @return \Elastica\Response Response
  388. *
  389. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html
  390. */
  391. public function addAlias($name, $replace = false)
  392. {
  393. $data = ['actions' => []];
  394. if ($replace) {
  395. $status = new Status($this->getClient());
  396. foreach ($status->getIndicesWithAlias($name) as $index) {
  397. $data['actions'][] = ['remove' => ['index' => $index->getName(), 'alias' => $name]];
  398. }
  399. }
  400. $data['actions'][] = ['add' => ['index' => $this->getName(), 'alias' => $name]];
  401. $endpoint = new Update();
  402. $endpoint->setBody($data);
  403. return $this->getClient()->requestEndpoint($endpoint);
  404. }
  405. /**
  406. * Removes an alias pointing to the current index.
  407. *
  408. * @param string $name Alias name
  409. *
  410. * @return \Elastica\Response Response
  411. *
  412. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html
  413. */
  414. public function removeAlias($name)
  415. {
  416. $endpoint = new \Elasticsearch\Endpoints\Indices\Alias\Delete();
  417. $endpoint->setName($name);
  418. return $this->requestEndpoint($endpoint);
  419. }
  420. /**
  421. * Returns all index aliases.
  422. *
  423. * @return array Aliases
  424. */
  425. public function getAliases()
  426. {
  427. $endpoint = new \Elasticsearch\Endpoints\Indices\Alias\Get();
  428. $endpoint->setName('*');
  429. $responseData = $this->requestEndpoint($endpoint)->getData();
  430. if (!isset($responseData[$this->getName()])) {
  431. return [];
  432. }
  433. $data = $responseData[$this->getName()];
  434. if (!empty($data['aliases'])) {
  435. return array_keys($data['aliases']);
  436. }
  437. return [];
  438. }
  439. /**
  440. * Checks if the index has the given alias.
  441. *
  442. * @param string $name Alias name
  443. *
  444. * @return bool
  445. */
  446. public function hasAlias($name)
  447. {
  448. return in_array($name, $this->getAliases());
  449. }
  450. /**
  451. * Clears the cache of an index.
  452. *
  453. * @return \Elastica\Response Response object
  454. *
  455. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-clearcache.html
  456. */
  457. public function clearCache()
  458. {
  459. // TODO: add additional cache clean arguments
  460. return $this->requestEndpoint(new Clear());
  461. }
  462. /**
  463. * Flushes the index to storage.
  464. *
  465. * @param array $options
  466. *
  467. * @return Response Response object
  468. *
  469. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-flush.html
  470. */
  471. public function flush(array $options = [])
  472. {
  473. $endpoint = new Flush();
  474. $endpoint->setParams($options);
  475. return $this->requestEndpoint($endpoint);
  476. }
  477. /**
  478. * Can be used to change settings during runtime. One example is to use it for bulk updating.
  479. *
  480. * @param array $data Data array
  481. *
  482. * @return \Elastica\Response Response object
  483. *
  484. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html
  485. */
  486. public function setSettings(array $data)
  487. {
  488. $endpoint = new Put();
  489. $endpoint->setBody($data);
  490. return $this->requestEndpoint($endpoint);
  491. }
  492. /**
  493. * Makes calls to the elasticsearch server based on this index.
  494. *
  495. * @param string $path Path to call
  496. * @param string $method Rest method to use (GET, POST, DELETE, PUT)
  497. * @param array|string $data OPTIONAL Arguments as array or encoded string
  498. * @param array $query OPTIONAL Query params
  499. *
  500. * @return \Elastica\Response Response object
  501. */
  502. public function request($path, $method, $data = [], array $query = [])
  503. {
  504. $path = $this->getName().'/'.$path;
  505. return $this->getClient()->request($path, $method, $data, $query);
  506. }
  507. /**
  508. * Makes calls to the elasticsearch server with usage official client Endpoint based on this index.
  509. *
  510. * @param AbstractEndpoint $endpoint
  511. *
  512. * @return Response
  513. */
  514. public function requestEndpoint(AbstractEndpoint $endpoint)
  515. {
  516. $cloned = clone $endpoint;
  517. $cloned->setIndex($this->getName());
  518. return $this->getClient()->requestEndpoint($cloned);
  519. }
  520. /**
  521. * Analyzes a string.
  522. *
  523. * Detailed arguments can be found here in the link
  524. *
  525. * @param array $body String to be analyzed
  526. * @param array $args OPTIONAL Additional arguments
  527. *
  528. * @return array Server response
  529. *
  530. * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-analyze.html
  531. */
  532. public function analyze(array $body, $args = [])
  533. {
  534. $endpoint = new Analyze();
  535. $endpoint->setBody($body);
  536. $endpoint->setParams($args);
  537. $data = $this->requestEndpoint($endpoint)->getData();
  538. // Support for "Explain" parameter, that returns a different response structure from Elastic
  539. // @see: https://www.elastic.co/guide/en/elasticsearch/reference/current/_explain_analyze.html
  540. if (isset($body['explain']) && $body['explain']) {
  541. return $data['detail'];
  542. }
  543. return $data['tokens'];
  544. }
  545. }