Completed
Push — master ( d45df9...7fc86e )
by Nicolas
02:28
created

Index   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 582
Duplicated Lines 3.95 %

Coupling/Cohesion

Components 1
Dependencies 30

Importance

Changes 0
Metric Value
wmc 53
lcom 1
cbo 30
dl 23
loc 582
rs 6.96
c 0
b 0
f 0

33 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 9 9 2
A getType() 0 4 1
A getStats() 0 4 1
A getRecovery() 0 4 1
A getMapping() 14 14 2
A getSettings() 0 4 1
A updateDocuments() 0 8 2
A updateByQuery() 0 15 2
A addDocuments() 0 8 2
A deleteByQuery() 0 10 2
A delete() 0 4 1
A deleteDocuments() 0 8 2
A forcemerge() 0 7 1
A refresh() 0 4 1
B create() 0 30 8
A exists() 0 6 1
A createSearch() 0 8 1
A search() 0 6 1
A count() 0 6 1
A open() 0 4 1
A close() 0 4 1
A getName() 0 4 1
A getClient() 0 4 1
A addAlias() 0 18 3
A removeAlias() 0 7 1
A getAliases() 0 18 3
A hasAlias() 0 4 1
A clearCache() 0 5 1
A flush() 0 7 1
A setSettings() 0 7 1
A request() 0 6 1
A requestEndpoint() 0 7 1
A analyze() 0 16 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Index often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Index, and based on these observations, apply Extract Interface, too.

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