Completed
Push — master ( aee63e...997989 )
by Nicolas
02:11
created

Index::getStats()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
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\Aliases\Update;
20
use Elasticsearch\Endpoints\Indices\Analyze;
21
use Elasticsearch\Endpoints\Indices\Cache\Clear;
22
use Elasticsearch\Endpoints\Indices\Close;
23
use Elasticsearch\Endpoints\Indices\Create;
24
use Elasticsearch\Endpoints\Indices\Delete;
25
use Elasticsearch\Endpoints\Indices\Exists;
26
use Elasticsearch\Endpoints\Indices\Flush;
27
use Elasticsearch\Endpoints\Indices\ForceMerge;
28
use Elasticsearch\Endpoints\Indices\Mapping\Get as MappingGet;
29
use Elasticsearch\Endpoints\Indices\Open;
30
use Elasticsearch\Endpoints\Indices\Refresh;
31
use Elasticsearch\Endpoints\Indices\Settings\Put;
32
use Elasticsearch\Endpoints\UpdateByQuery;
33
34
/**
35
 * Elastica index object.
36
 *
37
 * Handles reads, deletes and configurations of an index
38
 *
39
 * @author   Nicolas Ruflin <[email protected]>
40
 */
41
class Index implements SearchableInterface
42
{
43
    /**
44
     * Index name.
45
     *
46
     * @var string Index name
47
     */
48
    protected $_name;
49
50
    /**
51
     * Client object.
52
     *
53
     * @var Client Client object
54
     */
55
    protected $_client;
56
57
    /**
58
     * Creates a new index object.
59
     *
60
     * All the communication to and from an index goes of this object
61
     *
62
     * @param Client $client Client object
63
     * @param string $name   Index name
64
     */
65
    public function __construct(Client $client, string $name)
66
    {
67
        $this->_client = $client;
68
        $this->_name = $name;
69
    }
70
71
    /**
72
     * Return Index Stats.
73
     *
74
     * @return \Elastica\Index\Stats
75
     */
76
    public function getStats()
77
    {
78
        return new IndexStats($this);
79
    }
80
81
    /**
82
     * Return Index Recovery.
83
     *
84
     * @return \Elastica\Index\Recovery
85
     */
86
    public function getRecovery()
87
    {
88
        return new IndexRecovery($this);
89
    }
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
     * @return Response
98
     */
99
    public function setMapping(Mapping $mapping, array $query = []): Response
100
    {
101
        return $mapping->send($this, $query);
102
    }
103
104
    /**
105
     * Gets all mappings for the current index.
106
     *
107
     * @return array
108
     */
109
    public function getMapping(): array
110
    {
111
        $response = $this->requestEndpoint(new MappingGet());
112
        $data = $response->getData();
113
114
        // Get first entry as if index is an Alias, the name of the mapping is the real name and not alias name
115
        $mapping = \array_shift($data);
116
117
        return $mapping['mappings'] ?? [];
118
    }
119
120
    /**
121
     * Returns the index settings object.
122
     *
123
     * @return \Elastica\Index\Settings Settings object
124
     */
125
    public function getSettings()
126
    {
127
        return new IndexSettings($this);
128
    }
129
130
    /**
131
     * @param string       $id
132
     * @param array|string $data
133
     *
134
     * @return Document
135
     */
136
    public function createDocument(string $id = '', $data = [])
137
    {
138
        return new Document($id, $data, $this);
139
    }
140
141
    /**
142
     * Uses _bulk to send documents to the server.
143
     *
144
     * @param Document[] $docs    Array of Elastica\Document
145
     * @param array      $options Array of query params to use for query. For possible options check es api
146
     *
147
     * @return ResponseSet
148
     *
149
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
150
     */
151
    public function updateDocuments(array $docs, array $options = [])
152
    {
153
        foreach ($docs as $doc) {
154
            $doc->setIndex($this->getName());
155
        }
156
157
        return $this->getClient()->updateDocuments($docs, $options);
158
    }
159
160
    /**
161
     * Update entries in the db based on a query.
162
     *
163
     * @param Query|string|array $query   Query object or array
164
     * @param AbstractScript     $script  Script
165
     * @param array              $options Optional params
166
     *
167
     * @return Response
168
     *
169
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update-by-query.html
170
     */
171
    public function updateByQuery($query, AbstractScript $script, array $options = [])
172
    {
173
        $endpoint = new UpdateByQuery();
174
        $q = Query::create($query)->getQuery();
175
        $body = [
176
            'query' => \is_array($q) ? $q : $q->toArray(),
177
            'script' => $script->toArray()['script'],
178
        ];
179
180
        $endpoint->setBody($body);
181
        $endpoint->setParams($options);
182
183
        return $this->requestEndpoint($endpoint);
184
    }
185
186
    /**
187
     * Adds the given document to the search index.
188
     *
189
     * @param Document $doc Document with data
190
     *
191
     * @return Response
192
     */
193
    public function addDocument(Document $doc)
194
    {
195
        $endpoint = new IndexEndpoint();
196
197
        if (null !== $doc->getId() && '' !== $doc->getId()) {
198
            $endpoint->setID($doc->getId());
199
        }
200
201
        $options = $doc->getOptions(
202
            [
203
                'version',
204
                'version_type',
205
                'routing',
206
                'percolate',
207
                'parent',
208
                'op_type',
209
                'consistency',
210
                'replication',
211
                'refresh',
212
                'timeout',
213
                'pipeline',
214
            ]
215
        );
216
217
        $endpoint->setBody($doc->getData());
218
        $endpoint->setParams($options);
219
220
        $response = $this->requestEndpoint($endpoint);
221
222
        $data = $response->getData();
223
        // set autogenerated id to document
224 View Code Duplication
        if ($response->isOk() && (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
225
            $doc->isAutoPopulate() || $this->getClient()->getConfigValue(['document', 'autoPopulate'], false)
226
        )) {
227
            if (isset($data['_id']) && !$doc->hasId()) {
228
                $doc->setId($data['_id']);
229
            }
230
            if (isset($data['_version'])) {
231
                $doc->setVersion($data['_version']);
232
            }
233
        }
234
235
        return $response;
236
    }
237
238
    /**
239
     * Uses _bulk to send documents to the server.
240
     *
241
     * @param array|Document[] $docs    Array of Elastica\Document
242
     * @param array            $options Array of query params to use for query. For possible options check es api
243
     *
244
     * @return ResponseSet
245
     *
246
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
247
     */
248
    public function addDocuments(array $docs, array $options = [])
249
    {
250
        foreach ($docs as $doc) {
251
            $doc->setIndex($this->getName());
252
        }
253
254
        return $this->getClient()->addDocuments($docs, $options);
255
    }
256
257
    /**
258
     * Get the document from search index.
259
     *
260
     * @param int|string $id      Document id
261
     * @param array      $options options for the get request
262
     *
263
     * @throws \Elastica\Exception\ResponseException
264
     * @throws NotFoundException
265
     *
266
     * @return Document
267
     */
268
    public function getDocument($id, array $options = []): Document
269
    {
270
        $endpoint = new DocumentGet();
271
        $endpoint->setID($id);
272
        $endpoint->setParams($options);
273
274
        $response = $this->requestEndpoint($endpoint);
275
        $result = $response->getData();
276
277
        if (!isset($result['found']) || false === $result['found']) {
278
            throw new NotFoundException('doc id '.$id.' not found');
279
        }
280
281
        if (isset($result['fields'])) {
282
            $data = $result['fields'];
283
        } elseif (isset($result['_source'])) {
284
            $data = $result['_source'];
285
        } else {
286
            $data = [];
287
        }
288
289
        $document = new Document($id, $data, $this->getName());
290
        $document->setVersion($result['_version']);
291
292
        return $document;
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
     * @param string $id      Document id
301
     * @param array  $options
302
     *
303
     * @throws NotFoundException
304
     *
305
     * @return Response Response object
306
     */
307
    public function deleteById(string $id, array $options = []): Response
308
    {
309
        if (!\trim($id)) {
310
            throw new NotFoundException('Doc id "'.$id.'" not found and can not be deleted');
311
        }
312
313
        $endpoint = new \Elasticsearch\Endpoints\Delete();
314
        $endpoint->setID(\trim($id));
315
        $endpoint->setParams($options);
316
317
        $response = $this->requestEndpoint($endpoint);
318
319
        $responseData = $response->getData();
320
321
        if (isset($responseData['result']) && 'not_found' === $responseData['result']) {
322
            throw new NotFoundException('Doc id "'.$id.'" not found and can not be deleted');
323
        }
324
325
        return $response;
326
    }
327
328
    /**
329
     * Deletes entries in the db based on a query.
330
     *
331
     * @param Query|AbstractQuery|string|array $query   Query object or array
332
     * @param array                            $options Optional params
333
     *
334
     * @return Response
335
     *
336
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.0/docs-delete-by-query.html
337
     */
338
    public function deleteByQuery($query, array $options = [])
339
    {
340
        $query = Query::create($query)->getQuery();
341
342
        $endpoint = new DeleteByQuery();
343
        $endpoint->setBody(['query' => \is_array($query) ? $query : $query->toArray()]);
344
        $endpoint->setParams($options);
345
346
        return $this->requestEndpoint($endpoint);
347
    }
348
349
    /**
350
     * Deletes the index.
351
     *
352
     * @return Response Response object
353
     */
354
    public function delete()
355
    {
356
        return $this->requestEndpoint(new Delete());
357
    }
358
359
    /**
360
     * Uses _bulk to delete documents from the server.
361
     *
362
     * @param array|Document[] $docs Array of Elastica\Document
363
     *
364
     * @return ResponseSet
365
     *
366
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
367
     */
368
    public function deleteDocuments(array $docs)
369
    {
370
        foreach ($docs as $doc) {
371
            $doc->setIndex($this->getName());
372
        }
373
374
        return $this->getClient()->deleteDocuments($docs);
375
    }
376
377
    /**
378
     * Force merges index.
379
     *
380
     * Detailed arguments can be found here in the link
381
     *
382
     * @param array $args OPTIONAL Additional arguments
383
     *
384
     * @return Response
385
     *
386
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-forcemerge.html
387
     */
388
    public function forcemerge($args = [])
389
    {
390
        $endpoint = new ForceMerge();
391
        $endpoint->setParams($args);
392
393
        return $this->requestEndpoint($endpoint);
394
    }
395
396
    /**
397
     * Refreshes the index.
398
     *
399
     * @return Response Response object
400
     *
401
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-refresh.html
402
     */
403
    public function refresh()
404
    {
405
        return $this->requestEndpoint(new Refresh());
406
    }
407
408
    /**
409
     * Creates a new index with the given arguments.
410
     *
411
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html
412
     *
413
     * @param array      $args    OPTIONAL Arguments to use
414
     * @param bool|array $options OPTIONAL
415
     *                            bool=> Deletes index first if already exists (default = false).
416
     *                            array => Associative array of options (option=>value)
417
     *
418
     * @throws InvalidException
419
     * @throws \Elastica\Exception\ResponseException
420
     *
421
     * @return Response Server response
422
     */
423
    public function create(array $args = [], $options = null)
424
    {
425
        if (\is_bool($options) && $options) {
426
            try {
427
                $this->delete();
428
            } catch (ResponseException $e) {
429
                // Table can't be deleted, because doesn't exist
430
            }
431
        } elseif (\is_array($options)) {
432
            foreach ($options as $key => $value) {
433
                switch ($key) {
434
                    case 'recreate':
435
                        try {
436
                            $this->delete();
437
                        } catch (ResponseException $e) {
438
                            // Table can't be deleted, because doesn't exist
439
                        }
440
                        break;
441
                    default:
442
                        throw new InvalidException('Invalid option '.$key);
443
                        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...
444
                }
445
            }
446
        }
447
448
        $endpoint = new Create();
449
        $endpoint->setBody($args);
450
451
        return $this->requestEndpoint($endpoint);
452
    }
453
454
    /**
455
     * Checks if the given index is already created.
456
     *
457
     * @return bool True if index exists
458
     */
459
    public function exists(): bool
460
    {
461
        $response = $this->requestEndpoint(new Exists());
462
463
        return 200 === $response->getStatus();
464
    }
465
466
    /**
467
     * @param string|array|Query $query
468
     * @param int|array          $options
469
     * @param BuilderInterface   $builder
470
     *
471
     * @return Search
472
     */
473
    public function createSearch($query = '', $options = null, BuilderInterface $builder = null)
474
    {
475
        $search = new Search($this->getClient(), $builder);
476
        $search->addIndex($this);
477
        $search->setOptionsAndQuery($options, $query);
478
479
        return $search;
480
    }
481
482
    /**
483
     * Searches in this index.
484
     *
485
     * @param string|array|Query $query   Array with all query data inside or a Elastica\Query object
486
     * @param int|array          $options OPTIONAL Limit or associative array of options (option=>value)
487
     * @param string             $method  OPTIONAL Request method (use const's) (default = Request::POST)
488
     *
489
     * @return ResultSet with all results inside
490
     *
491
     * @see \Elastica\SearchableInterface::search
492
     */
493
    public function search($query = '', $options = null, $method = Request::POST)
494
    {
495
        $search = $this->createSearch($query, $options);
496
497
        return $search->search('', null, $method);
498
    }
499
500
    /**
501
     * Counts results of query.
502
     *
503
     * @param string|array|Query $query  Array with all query data inside or a Elastica\Query object
504
     * @param string             $method OPTIONAL Request method (use const's) (default = Request::POST)
505
     *
506
     * @return int number of documents matching the query
507
     *
508
     * @see \Elastica\SearchableInterface::count
509
     */
510
    public function count($query = '', $method = Request::POST)
511
    {
512
        $search = $this->createSearch($query);
513
514
        return $search->count('', false, $method);
0 ignored issues
show
Bug Compatibility introduced by
The expression $search->count('', false, $method); of type Elastica\ResultSet|integer adds the type Elastica\ResultSet to the return on line 514 which is incompatible with the return type declared by the interface Elastica\SearchableInterface::count of type integer.
Loading history...
515
    }
516
517
    /**
518
     * Opens an index.
519
     *
520
     * @return Response Response object
521
     *
522
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-open-close.html
523
     */
524
    public function open()
525
    {
526
        return $this->requestEndpoint(new Open());
527
    }
528
529
    /**
530
     * Closes the index.
531
     *
532
     * @return Response Response object
533
     *
534
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-open-close.html
535
     */
536
    public function close()
537
    {
538
        return $this->requestEndpoint(new Close());
539
    }
540
541
    /**
542
     * Returns the index name.
543
     *
544
     * @return string Index name
545
     */
546
    public function getName(): string
547
    {
548
        return $this->_name;
549
    }
550
551
    /**
552
     * Returns index client.
553
     *
554
     * @return \Elastica\Client Index client object
555
     */
556
    public function getClient()
557
    {
558
        return $this->_client;
559
    }
560
561
    /**
562
     * Adds an alias to the current index.
563
     *
564
     * @param string $name    Alias name
565
     * @param bool   $replace OPTIONAL If set, an existing alias will be replaced
566
     *
567
     * @return Response Response
568
     *
569
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html
570
     */
571
    public function addAlias($name, $replace = false)
572
    {
573
        $data = ['actions' => []];
574
575
        if ($replace) {
576
            $status = new Status($this->getClient());
577
            foreach ($status->getIndicesWithAlias($name) as $index) {
578
                $data['actions'][] = ['remove' => ['index' => $index->getName(), 'alias' => $name]];
579
            }
580
        }
581
582
        $data['actions'][] = ['add' => ['index' => $this->getName(), 'alias' => $name]];
583
584
        $endpoint = new Update();
585
        $endpoint->setBody($data);
586
587
        return $this->getClient()->requestEndpoint($endpoint);
588
    }
589
590
    /**
591
     * Removes an alias pointing to the current index.
592
     *
593
     * @param string $name Alias name
594
     *
595
     * @return Response Response
596
     *
597
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html
598
     */
599
    public function removeAlias($name)
600
    {
601
        $endpoint = new \Elasticsearch\Endpoints\Indices\Alias\Delete();
602
        $endpoint->setName($name);
603
604
        return $this->requestEndpoint($endpoint);
605
    }
606
607
    /**
608
     * Returns all index aliases.
609
     *
610
     * @return array Aliases
611
     */
612
    public function getAliases()
613
    {
614
        $endpoint = new \Elasticsearch\Endpoints\Indices\Alias\Get();
615
        $endpoint->setName('*');
616
617
        $responseData = $this->requestEndpoint($endpoint)->getData();
618
619
        if (!isset($responseData[$this->getName()])) {
620
            return [];
621
        }
622
623
        $data = $responseData[$this->getName()];
624
        if (!empty($data['aliases'])) {
625
            return \array_keys($data['aliases']);
626
        }
627
628
        return [];
629
    }
630
631
    /**
632
     * Checks if the index has the given alias.
633
     *
634
     * @param string $name Alias name
635
     *
636
     * @return bool
637
     */
638
    public function hasAlias($name)
639
    {
640
        return \in_array($name, $this->getAliases());
641
    }
642
643
    /**
644
     * Clears the cache of an index.
645
     *
646
     * @return Response Response object
647
     *
648
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-clearcache.html
649
     */
650
    public function clearCache()
651
    {
652
        // TODO: add additional cache clean arguments
653
        return $this->requestEndpoint(new Clear());
654
    }
655
656
    /**
657
     * Flushes the index to storage.
658
     *
659
     * @param array $options
660
     *
661
     * @return Response Response object
662
     *
663
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-flush.html
664
     */
665
    public function flush(array $options = [])
666
    {
667
        $endpoint = new Flush();
668
        $endpoint->setParams($options);
669
670
        return $this->requestEndpoint($endpoint);
671
    }
672
673
    /**
674
     * Can be used to change settings during runtime. One example is to use it for bulk updating.
675
     *
676
     * @param array $data Data array
677
     *
678
     * @return Response Response object
679
     *
680
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html
681
     */
682
    public function setSettings(array $data)
683
    {
684
        $endpoint = new Put();
685
        $endpoint->setBody($data);
686
687
        return $this->requestEndpoint($endpoint);
688
    }
689
690
    /**
691
     * Makes calls to the elasticsearch server based on this index.
692
     *
693
     * @param string       $path   Path to call
694
     * @param string       $method Rest method to use (GET, POST, DELETE, PUT)
695
     * @param array|string $data   OPTIONAL Arguments as array or encoded string
696
     * @param array        $query  OPTIONAL Query params
697
     *
698
     * @return Response Response object
699
     */
700
    public function request($path, $method, $data = [], array $query = [])
701
    {
702
        $path = $this->getName().'/'.$path;
703
704
        return $this->getClient()->request($path, $method, $data, $query);
705
    }
706
707
    /**
708
     * Makes calls to the elasticsearch server with usage official client Endpoint based on this index.
709
     *
710
     * @param AbstractEndpoint $endpoint
711
     *
712
     * @return Response
713
     */
714
    public function requestEndpoint(AbstractEndpoint $endpoint)
715
    {
716
        $cloned = clone $endpoint;
717
        $cloned->setIndex($this->getName());
718
719
        return $this->getClient()->requestEndpoint($cloned);
720
    }
721
722
    /**
723
     * Run the analysis on the index.
724
     *
725
     * @param array $body request body for the `_analyze` API, see API documentation for the requried properties
726
     * @param array $args OPTIONAL Additional arguments
727
     *
728
     * @return array Server response
729
     *
730
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-analyze.html
731
     */
732
    public function analyze(array $body, $args = [])
733
    {
734
        $endpoint = new Analyze();
735
        $endpoint->setBody($body);
736
        $endpoint->setParams($args);
737
738
        $data = $this->requestEndpoint($endpoint)->getData();
739
740
        // Support for "Explain" parameter, that returns a different response structure from Elastic
741
        // @see: https://www.elastic.co/guide/en/elasticsearch/reference/current/_explain_analyze.html
742
        if (isset($body['explain']) && $body['explain']) {
743
            return $data['detail'];
744
        }
745
746
        return $data['tokens'];
747
    }
748
749
    /**
750
     * Update document, using update script.
751
     *
752
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html
753
     *
754
     * @param Document|AbstractScript $data    Document or Script with update data
755
     * @param array                   $options array of query params to use for query
756
     *
757
     * @throws InvalidException
758
     *
759
     * @return Response
760
     */
761
    public function updateDocument($data, array $options = [])
762
    {
763
        if (!($data instanceof Document) && !($data instanceof AbstractScript)) {
764
            throw new \InvalidArgumentException('Data should be a Document or Script');
765
        }
766
767
        if (!$data->hasId()) {
768
            throw new InvalidException('Document or Script id is not set');
769
        }
770
771
        return $this->getClient()->updateDocument($data->getId(), $data, $this->getName(), $options);
772
    }
773
}
774