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