Completed
Pull Request — master (#1803)
by thomas
02:21
created

Index::clearCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Elastica;
4
5
use Elastica\Bulk\ResponseSet;
6
use Elastica\Exception\InvalidException;
7
use Elastica\Exception\NotFoundException;
8
use Elastica\Exception\ResponseException;
9
use Elastica\Index\Recovery as IndexRecovery;
10
use Elastica\Index\Settings as IndexSettings;
11
use Elastica\Index\Stats as IndexStats;
12
use Elastica\Query\AbstractQuery;
13
use Elastica\ResultSet\BuilderInterface;
14
use Elastica\Script\AbstractScript;
15
use Elasticsearch\Endpoints\AbstractEndpoint;
16
use Elasticsearch\Endpoints\DeleteByQuery;
17
use Elasticsearch\Endpoints\Get as DocumentGet;
18
use Elasticsearch\Endpoints\Index as IndexEndpoint;
19
use Elasticsearch\Endpoints\Indices\Alias;
20
use Elasticsearch\Endpoints\Indices\Aliases\Update;
21
use Elasticsearch\Endpoints\Indices\Analyze;
22
use Elasticsearch\Endpoints\Indices\Cache\Clear;
23
use Elasticsearch\Endpoints\Indices\Close;
24
use Elasticsearch\Endpoints\Indices\Create;
25
use Elasticsearch\Endpoints\Indices\Delete;
26
use Elasticsearch\Endpoints\Indices\Exists;
27
use Elasticsearch\Endpoints\Indices\Flush;
28
use Elasticsearch\Endpoints\Indices\ForceMerge;
29
use Elasticsearch\Endpoints\Indices\Mapping\Get as MappingGet;
30
use Elasticsearch\Endpoints\Indices\Open;
31
use Elasticsearch\Endpoints\Indices\Refresh;
32
use Elasticsearch\Endpoints\Indices\Settings\Put;
33
use Elasticsearch\Endpoints\UpdateByQuery;
34
35
/**
36
 * Elastica index object.
37
 *
38
 * Handles reads, deletes and configurations of an index
39
 *
40
 * @author   Nicolas Ruflin <[email protected]>
41
 */
42
class Index implements SearchableInterface
43
{
44
    /**
45
     * Index name.
46
     *
47
     * @var string Index name
48
     */
49
    protected $_name;
50
51
    /**
52
     * Client object.
53
     *
54
     * @var Client Client object
55
     */
56
    protected $_client;
57
58
    /**
59
     * Creates a new index object.
60
     *
61
     * All the communication to and from an index goes of this object
62
     *
63
     * @param Client $client Client object
64
     * @param string $name   Index name
65
     */
66
    public function __construct(Client $client, string $name)
67
    {
68
        $this->_client = $client;
69
        $this->_name = $name;
70
    }
71
72
    /**
73
     * Return Index Stats.
74
     *
75
     * @return \Elastica\Index\Stats
76
     */
77
    public function getStats()
78
    {
79
        return new IndexStats($this);
80
    }
81
82
    /**
83
     * Return Index Recovery.
84
     *
85
     * @return \Elastica\Index\Recovery
86
     */
87
    public function getRecovery()
88
    {
89
        return new IndexRecovery($this);
90
    }
91
92
    /**
93
     * Sets the mappings for the current index.
94
     *
95
     * @param Mapping $mapping MappingType object
96
     * @param array   $query   querystring when put mapping (for example update_all_types)
97
     */
98
    public function setMapping(Mapping $mapping, array $query = []): Response
99
    {
100
        return $mapping->send($this, $query);
101
    }
102
103
    /**
104
     * Gets all mappings for the current index.
105
     */
106
    public function getMapping(): array
107
    {
108
        $response = $this->requestEndpoint(new MappingGet());
109
        $data = $response->getData();
110
111
        // Get first entry as if index is an Alias, the name of the mapping is the real name and not alias name
112
        $mapping = \array_shift($data);
113
114
        return $mapping['mappings'] ?? [];
115
    }
116
117
    /**
118
     * Returns the index settings object.
119
     *
120
     * @return \Elastica\Index\Settings Settings object
121
     */
122
    public function getSettings()
123
    {
124
        return new IndexSettings($this);
125
    }
126
127
    /**
128
     * @param array|string $data
129
     *
130
     * @return Document
131
     */
132
    public function createDocument(string $id = '', $data = [])
133
    {
134
        return new Document($id, $data, $this);
135
    }
136
137
    /**
138
     * Uses _bulk to send documents to the server.
139
     *
140
     * @param Document[] $docs    Array of Elastica\Document
141
     * @param array      $options Array of query params to use for query. For possible options check es api
142
     *
143
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
144
     */
145
    public function updateDocuments(array $docs, array $options = []): ResponseSet
146
    {
147
        foreach ($docs as $doc) {
148
            $doc->setIndex($this->getName());
149
        }
150
151
        return $this->getClient()->updateDocuments($docs, $options);
152
    }
153
154
    /**
155
     * Update entries in the db based on a query.
156
     *
157
     * @param array|Query|string $query   Query object or array
158
     * @param AbstractScript     $script  Script
159
     * @param array              $options Optional params
160
     *
161
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update-by-query.html
162
     */
163
    public function updateByQuery($query, AbstractScript $script, array $options = []): Response
164
    {
165
        $endpoint = new UpdateByQuery();
166
        $q = Query::create($query)->getQuery();
167
        $body = [
168
            'query' => \is_array($q) ? $q : $q->toArray(),
169
            'script' => $script->toArray()['script'],
170
        ];
171
172
        $endpoint->setBody($body);
173
        $endpoint->setParams($options);
174
175
        return $this->requestEndpoint($endpoint);
176
    }
177
178
    /**
179
     * Adds the given document to the search index.
180
     */
181
    public function addDocument(Document $doc): Response
182
    {
183
        $endpoint = new IndexEndpoint();
184
185
        if (null !== $doc->getId() && '' !== $doc->getId()) {
186
            $endpoint->setID($doc->getId());
187
        }
188
189
        $options = $doc->getOptions(
190
            [
191
                'version',
192
                'version_type',
193
                'routing',
194
                'percolate',
195
                'parent',
196
                'op_type',
197
                'consistency',
198
                'replication',
199
                'refresh',
200
                'timeout',
201
                'pipeline',
202
            ]
203
        );
204
205
        $endpoint->setBody($doc->getData());
206
        $endpoint->setParams($options);
207
208
        $response = $this->requestEndpoint($endpoint);
209
210
        $data = $response->getData();
211
        // set autogenerated id to document
212
        if ($response->isOk() && (
213
            $doc->isAutoPopulate() || $this->getClient()->getConfigValue(['document', 'autoPopulate'], false)
214
        )) {
215
            if (isset($data['_id']) && !$doc->hasId()) {
216
                $doc->setId($data['_id']);
217
            }
218
            if (isset($data['_version'])) {
219
                $doc->setVersion($data['_version']);
220
            }
221
            if (isset($data['_seq_no'])) {
222
                $doc->setSequenceNumber($data['_seq_no']);
223
            }
224
            if (isset($data['_primary_term'])) {
225
                $doc->setPrimaryTerm($data['_primary_term']);
226
            }
227
        }
228
229
        return $response;
230
    }
231
232
    /**
233
     * Uses _bulk to send documents to the server.
234
     *
235
     * @param array|Document[] $docs    Array of Elastica\Document
236
     * @param array            $options Array of query params to use for query. For possible options check es api
237
     *
238
     * @return ResponseSet
239
     *
240
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
241
     */
242
    public function addDocuments(array $docs, array $options = [])
243
    {
244
        foreach ($docs as $doc) {
245
            $doc->setIndex($this->getName());
246
        }
247
248
        return $this->getClient()->addDocuments($docs, $options);
249
    }
250
251
    /**
252
     * Get the document from search index.
253
     *
254
     * @param int|string $id      Document id
255
     * @param array      $options options for the get request
256
     *
257
     * @throws \Elastica\Exception\ResponseException
258
     * @throws NotFoundException
259
     */
260
    public function getDocument($id, array $options = []): Document
261
    {
262
        $endpoint = new DocumentGet();
263
        $endpoint->setID($id);
264
        $endpoint->setParams($options);
265
266
        $response = $this->requestEndpoint($endpoint);
267
        $result = $response->getData();
268
269
        if (!isset($result['found']) || false === $result['found']) {
270
            throw new NotFoundException('doc id '.$id.' not found');
271
        }
272
273
        if (isset($result['fields'])) {
274
            $data = $result['fields'];
275
        } elseif (isset($result['_source'])) {
276
            $data = $result['_source'];
277
        } else {
278
            $data = [];
279
        }
280
281
        $doc = new Document($id, $data, $this->getName());
282
        if (isset($data['_version'])) {
283
            $doc->setVersion($data['_version']);
284
        }
285
        if (isset($data['_seq_no'])) {
286
            $doc->setSequenceNumber($data['_seq_no']);
287
        }
288
        if (isset($data['_primary_term'])) {
289
            $doc->setPrimaryTerm($data['_primary_term']);
290
        }
291
292
        return $doc;
293
    }
294
295
    /**
296
     * Deletes a document by its unique identifier.
297
     *
298
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete.html
299
     */
300
    public function deleteById(string $id, array $options = []): Response
301
    {
302
        if (!\trim($id)) {
303
            throw new NotFoundException('Doc id "'.$id.'" not found and can not be deleted');
304
        }
305
306
        $endpoint = new \Elasticsearch\Endpoints\Delete();
307
        $endpoint->setID(\trim($id));
308
        $endpoint->setParams($options);
309
310
        return $this->requestEndpoint($endpoint);
311
    }
312
313
    /**
314
     * Deletes documents matching the given query.
315
     *
316
     * @param AbstractQuery|array|Query|string $query   Query object or array
317
     * @param array                            $options Optional params
318
     *
319
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html
320
     */
321
    public function deleteByQuery($query, array $options = []): Response
322
    {
323
        $query = Query::create($query)->getQuery();
324
325
        $endpoint = new DeleteByQuery();
326
        $endpoint->setBody(['query' => \is_array($query) ? $query : $query->toArray()]);
327
        $endpoint->setParams($options);
328
329
        return $this->requestEndpoint($endpoint);
330
    }
331
332
    /**
333
     * Deletes the index.
334
     */
335
    public function delete(): Response
336
    {
337
        return $this->requestEndpoint(new Delete());
338
    }
339
340
    /**
341
     * Uses the "_bulk" endpoint to delete documents from the server.
342
     *
343
     * @param Document[] $docs Array of documents
344
     *
345
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
346
     */
347
    public function deleteDocuments(array $docs): ResponseSet
348
    {
349
        foreach ($docs as $doc) {
350
            $doc->setIndex($this->getName());
351
        }
352
353
        return $this->getClient()->deleteDocuments($docs);
354
    }
355
356
    /**
357
     * Force merges index.
358
     *
359
     * Detailed arguments can be found here in the ES documentation.
360
     *
361
     * @param array $args Additional arguments
362
     *
363
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-forcemerge.html
364
     */
365
    public function forcemerge($args = []): Response
366
    {
367
        $endpoint = new ForceMerge();
368
        $endpoint->setParams($args);
369
370
        return $this->requestEndpoint($endpoint);
371
    }
372
373
    /**
374
     * Refreshes the index.
375
     *
376
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-refresh.html
377
     */
378
    public function refresh(): Response
379
    {
380
        return $this->requestEndpoint(new Refresh());
381
    }
382
383
    /**
384
     * Creates a new index with the given arguments.
385
     *
386
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html
387
     *
388
     * @param array      $args    Additional arguments to pass to the Create endpoint
389
     * @param array|bool $options OPTIONAL
390
     *                            bool=> Deletes index first if already exists (default = false).
391
     *                            array => Associative array of options (option=>value)
392
     *
393
     * @throws InvalidException
394
     * @throws \Elastica\Exception\ResponseException
395
     *
396
     * @return Response Server response
397
     */
398
    public function create(array $args = [], $options = null): Response
399
    {
400
        if (\is_bool($options) && $options) {
401
            try {
402
                $this->delete();
403
            } catch (ResponseException $e) {
404
                // Table can't be deleted, because doesn't exist
405
            }
406
        } elseif (\is_array($options)) {
407
            foreach ($options as $key => $value) {
408
                switch ($key) {
409
                    case 'recreate':
410
                        try {
411
                            $this->delete();
412
                        } catch (ResponseException $e) {
413
                            // Table can't be deleted, because doesn't exist
414
                        }
415
                        break;
416
                    default:
417
                        throw new InvalidException('Invalid option '.$key);
418
                        break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
419
                }
420
            }
421
        }
422
423
        $endpoint = new Create();
424
        $endpoint->setBody($args);
425
426
        return $this->requestEndpoint($endpoint);
427
    }
428
429
    /**
430
     * Checks if the given index exists ans is created.
431
     */
432
    public function exists(): bool
433
    {
434
        $response = $this->requestEndpoint(new Exists());
435
436
        return 200 === $response->getStatus();
437
    }
438
439
    /**
440
     * @param array|Query|string $query
441
     * @param array|int          $options
442
     * @param BuilderInterface   $builder
443
     */
444
    public function createSearch($query = '', $options = null, ?BuilderInterface $builder = null): Search
445
    {
446
        $search = new Search($this->getClient(), $builder);
447
        $search->addIndex($this);
448
        $search->setOptionsAndQuery($options, $query);
449
450
        return $search;
451
    }
452
453
    /**
454
     * Searches in this index.
455
     *
456
     * @param array|Query|string $query   Array with all query data inside or a Elastica\Query object
457
     * @param array|int          $options Limit or associative array of options (option=>value)
458
     * @param string             $method  Request method, see Request's constants
459
     *
460
     * @see \Elastica\SearchableInterface::search
461
     */
462
    public function search($query = '', $options = null, string $method = Request::POST): ResultSet
463
    {
464
        $search = $this->createSearch($query, $options);
465
466
        return $search->search('', null, $method);
467
    }
468
469
    /**
470
     * Counts results of query.
471
     *
472
     * @param array|Query|string $query  Array with all query data inside or a Elastica\Query object
473
     * @param string             $method Request method, see Request's constants
474
     *
475
     * @see \Elastica\SearchableInterface::count
476
     */
477
    public function count($query = '', string $method = Request::POST): int
478
    {
479
        $search = $this->createSearch($query);
480
481
        return $search->count('', false, $method);
482
    }
483
484
    /**
485
     * Opens an index.
486
     *
487
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-open-close.html
488
     */
489
    public function open(): Response
490
    {
491
        return $this->requestEndpoint(new Open());
492
    }
493
494
    /**
495
     * Closes the index.
496
     *
497
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-open-close.html
498
     */
499
    public function close(): Response
500
    {
501
        return $this->requestEndpoint(new Close());
502
    }
503
504
    /**
505
     * Returns the index name.
506
     */
507
    public function getName(): string
508
    {
509
        return $this->_name;
510
    }
511
512
    /**
513
     * Returns index client.
514
     */
515
    public function getClient(): Client
516
    {
517
        return $this->_client;
518
    }
519
520
    /**
521
     * Adds an alias to the current index.
522
     *
523
     * @param bool $replace If set, an existing alias will be replaced
524
     *
525
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html
526
     */
527
    public function addAlias(string $name, bool $replace = false): Response
528
    {
529
        $data = ['actions' => []];
530
531
        if ($replace) {
532
            $status = new Status($this->getClient());
533
            foreach ($status->getIndicesWithAlias($name) as $index) {
534
                $data['actions'][] = ['remove' => ['index' => $index->getName(), 'alias' => $name]];
535
            }
536
        }
537
538
        $data['actions'][] = ['add' => ['index' => $this->getName(), 'alias' => $name]];
539
540
        $endpoint = new Update();
541
        $endpoint->setBody($data);
542
543
        return $this->getClient()->requestEndpoint($endpoint);
544
    }
545
546
    /**
547
     * Removes an alias pointing to the current index.
548
     *
549
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html
550
     */
551
    public function removeAlias(string $name): Response
552
    {
553
        $endpoint = new Alias\Delete();
554
        $endpoint->setName($name);
555
556
        return $this->requestEndpoint($endpoint);
557
    }
558
559
    /**
560
     * Returns all index aliases.
561
     *
562
     * @return string[]
563
     */
564
    public function getAliases(): array
565
    {
566
        $endpoint = new Alias\Get();
567
        $endpoint->setName('*');
568
569
        $responseData = $this->requestEndpoint($endpoint)->getData();
570
571
        if (!isset($responseData[$this->getName()])) {
572
            return [];
573
        }
574
575
        $data = $responseData[$this->getName()];
576
        if (!empty($data['aliases'])) {
577
            return \array_keys($data['aliases']);
578
        }
579
580
        return [];
581
    }
582
583
    /**
584
     * Checks if the index has the given alias.
585
     */
586
    public function hasAlias(string $name): bool
587
    {
588
        return \in_array($name, $this->getAliases(), true);
589
    }
590
591
    /**
592
     * Clears the cache of an index.
593
     *
594
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-clearcache.html
595
     */
596
    public function clearCache(): Response
597
    {
598
        // TODO: add additional cache clean arguments
599
        return $this->requestEndpoint(new Clear());
600
    }
601
602
    /**
603
     * Flushes the index to storage.
604
     *
605
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-flush.html
606
     */
607
    public function flush(array $options = []): Response
608
    {
609
        $endpoint = new Flush();
610
        $endpoint->setParams($options);
611
612
        return $this->requestEndpoint($endpoint);
613
    }
614
615
    /**
616
     * Can be used to change settings during runtime. One example is to use it for bulk updating.
617
     *
618
     * @param array $data Data array
619
     *
620
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html
621
     */
622
    public function setSettings(array $data): Response
623
    {
624
        $endpoint = new Put();
625
        $endpoint->setBody($data);
626
627
        return $this->requestEndpoint($endpoint);
628
    }
629
630
    /**
631
     * Makes calls to the elasticsearch server based on this index.
632
     *
633
     * @param string       $path   Path to call
634
     * @param string       $method Rest method to use (GET, POST, DELETE, PUT)
635
     * @param array|string $data   Arguments as array or encoded string
636
     */
637
    public function request(string $path, string $method, $data = [], array $queryParameters = []): Response
638
    {
639
        $path = $this->getName().'/'.$path;
640
641
        return $this->getClient()->request($path, $method, $data, $queryParameters);
642
    }
643
644
    /**
645
     * Makes calls to the elasticsearch server with usage official client Endpoint based on this index.
646
     */
647
    public function requestEndpoint(AbstractEndpoint $endpoint): Response
648
    {
649
        $cloned = clone $endpoint;
650
        $cloned->setIndex($this->getName());
651
652
        return $this->getClient()->requestEndpoint($cloned);
653
    }
654
655
    /**
656
     * Run the analysis on the index.
657
     *
658
     * @param array $body request body for the `_analyze` API, see API documentation for the required properties
659
     * @param array $args Additional arguments
660
     *
661
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-analyze.html
662
     */
663
    public function analyze(array $body, $args = []): array
664
    {
665
        $endpoint = new Analyze();
666
        $endpoint->setBody($body);
667
        $endpoint->setParams($args);
668
669
        $data = $this->requestEndpoint($endpoint)->getData();
670
671
        // Support for "Explain" parameter, that returns a different response structure from Elastic
672
        // @see: https://www.elastic.co/guide/en/elasticsearch/reference/current/_explain_analyze.html
673
        if (isset($body['explain']) && $body['explain']) {
674
            return $data['detail'];
675
        }
676
677
        return $data['tokens'];
678
    }
679
680
    /**
681
     * Update document, using update script.
682
     *
683
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html
684
     *
685
     * @param AbstractScript|Document $data    Document or Script with update data
686
     * @param array                   $options array of query params to use for query
687
     */
688
    public function updateDocument($data, array $options = []): Response
689
    {
690
        if (!($data instanceof Document) && !($data instanceof AbstractScript)) {
691
            throw new \InvalidArgumentException('Data should be a Document or Script');
692
        }
693
694
        if (!$data->hasId()) {
695
            throw new InvalidException('Document or Script id is not set');
696
        }
697
698
        return $this->getClient()->updateDocument($data->getId(), $data, $this->getName(), $options);
699
    }
700
}
701