Completed
Push — master ( 55b02b...fcf2bc )
by Nicolas
03:30
created

Type   D

Complexity

Total Complexity 52

Size/Duplication

Total Lines 526
Duplicated Lines 2.28 %

Coupling/Cohesion

Components 1
Dependencies 17

Importance

Changes 0
Metric Value
wmc 52
lcom 1
cbo 17
dl 12
loc 526
rs 4.9781
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A setSerializer() 0 6 1
A updateDocuments() 0 8 2
A addDocuments() 0 8 2
A addObjects() 0 17 3
A search() 0 6 1
A count() 0 6 1
A getIndex() 0 4 1
A deleteDocuments() 0 8 2
A createSearch() 0 7 1
A deleteDocument() 0 17 1
A __construct() 0 5 1
C addDocument() 0 46 9
A addObject() 0 14 3
A updateDocument() 0 18 4
B getDocument() 0 26 5
A createDocument() 0 7 1
A getName() 0 4 1
A setMapping() 0 7 1
A getMapping() 12 12 2
B deleteById() 0 20 5
A deleteIds() 0 4 1
A deleteByQuery() 0 10 1
A request() 0 6 1
A requestEndpoint() 0 6 1
A exists() 0 6 1

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 Type 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 Type, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Elastica;
3
4
use Elastica\Exception\InvalidException;
5
use Elastica\Exception\NotFoundException;
6
use Elastica\Exception\RuntimeException;
7
use Elastica\ResultSet\BuilderInterface;
8
use Elastica\Script\AbstractScript;
9
use Elastica\Type\Mapping;
10
use Elasticsearch\Endpoints\AbstractEndpoint;
11
use Elasticsearch\Endpoints\Delete;
12
use Elasticsearch\Endpoints\DeleteByQuery;
13
use Elasticsearch\Endpoints\Indices\Mapping\Get;
14
use Elasticsearch\Endpoints\Indices\Type\Exists;
15
16
/**
17
 * Elastica type object.
18
 *
19
 * elasticsearch has for every types as a substructure. This object
20
 * represents a type inside a context
21
 * The hierarchy is as following: client -> index -> type -> document
22
 *
23
 * @author   Nicolas Ruflin <[email protected]>
24
 */
25
class Type implements SearchableInterface
26
{
27
    /**
28
     * Index.
29
     *
30
     * @var \Elastica\Index Index object
31
     */
32
    protected $_index;
33
34
    /**
35
     * Type name.
36
     *
37
     * @var string Type name
38
     */
39
    protected $_name;
40
41
    /**
42
     * @var array|string A callable that serializes an object passed to it
43
     */
44
    protected $_serializer;
45
46
    /**
47
     * Creates a new type object inside the given index.
48
     *
49
     * @param \Elastica\Index $index Index Object
50
     * @param string          $name  Type name
51
     */
52
    public function __construct(Index $index, $name)
53
    {
54
        $this->_index = $index;
55
        $this->_name = $name;
56
    }
57
58
    /**
59
     * Adds the given document to the search index.
60
     *
61
     * @param \Elastica\Document $doc Document with data
62
     *
63
     * @return \Elastica\Response
64
     */
65
    public function addDocument(Document $doc)
66
    {
67
        $endpoint = new \Elasticsearch\Endpoints\Index();
68
69
        if (null !== $doc->getId() && '' !== $doc->getId()) {
70
            $endpoint->setID($doc->getId());
71
        }
72
73
        $options = $doc->getOptions(
74
            [
75
                'version',
76
                'version_type',
77
                'routing',
78
                'percolate',
79
                'parent',
80
                'op_type',
81
                'consistency',
82
                'replication',
83
                'refresh',
84
                'timeout',
85
            ]
86
        );
87
88
        $endpoint->setBody($doc->getData());
0 ignored issues
show
Bug introduced by
It seems like $doc->getData() targeting Elastica\Document::getData() can also be of type string; however, Elasticsearch\Endpoints\Index::setBody() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
89
        $endpoint->setParams($options);
90
91
        $response = $this->requestEndpoint($endpoint);
92
93
        $data = $response->getData();
94
        // set autogenerated id to document
95
        if (($doc->isAutoPopulate()
96
                || $this->getIndex()->getClient()->getConfigValue(['document', 'autoPopulate'], false))
97
            && $response->isOk()
98
        ) {
99
            if (!$doc->hasId()) {
100
                if (isset($data['_id'])) {
101
                    $doc->setId($data['_id']);
102
                }
103
            }
104
            if (isset($data['_version'])) {
105
                $doc->setVersion($data['_version']);
106
            }
107
        }
108
109
        return $response;
110
    }
111
112
    /**
113
     * @param $object
114
     * @param Document $doc
115
     *
116
     * @throws Exception\RuntimeException
117
     *
118
     * @return Response
119
     */
120
    public function addObject($object, Document $doc = null)
121
    {
122
        if (!isset($this->_serializer)) {
123
            throw new RuntimeException('No serializer defined');
124
        }
125
126
        $data = call_user_func($this->_serializer, $object);
127
        if (!$doc) {
128
            $doc = new Document();
129
        }
130
        $doc->setData($data);
131
132
        return $this->addDocument($doc);
133
    }
134
135
    /**
136
     * Update document, using update script. Requires elasticsearch >= 0.19.0.
137
     *
138
     * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html
139
     *
140
     * @param \Elastica\Document|\Elastica\Script\AbstractScript $data    Document with update data
141
     * @param array                                              $options array of query params to use for query. For possible options check es api
142
     *
143
     * @throws \Elastica\Exception\InvalidException
144
     *
145
     * @return \Elastica\Response
146
     */
147
    public function updateDocument($data, array $options = [])
148
    {
149
        if (!($data instanceof Document) && !($data instanceof AbstractScript)) {
150
            throw new \InvalidArgumentException('Data should be a Document or Script');
151
        }
152
153
        if (!$data->hasId()) {
154
            throw new InvalidException('Document or Script id is not set');
155
        }
156
157
        return $this->getIndex()->getClient()->updateDocument(
158
            $data->getId(),
159
            $data,
160
            $this->getIndex()->getName(),
161
            $this->getName(),
162
            $options
163
        );
164
    }
165
166
    /**
167
     * Uses _bulk to send documents to the server.
168
     *
169
     * @param array|\Elastica\Document[] $docs Array of Elastica\Document
170
     *
171
     * @return \Elastica\Bulk\ResponseSet
172
     *
173
     * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
174
     */
175
    public function updateDocuments(array $docs)
176
    {
177
        foreach ($docs as $doc) {
178
            $doc->setType($this->getName());
179
        }
180
181
        return $this->getIndex()->updateDocuments($docs);
182
    }
183
184
    /**
185
     * Uses _bulk to send documents to the server.
186
     *
187
     * @param array|\Elastica\Document[] $docs Array of Elastica\Document
188
     *
189
     * @return \Elastica\Bulk\ResponseSet
190
     *
191
     * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
192
     */
193
    public function addDocuments(array $docs)
194
    {
195
        foreach ($docs as $doc) {
196
            $doc->setType($this->getName());
197
        }
198
199
        return $this->getIndex()->addDocuments($docs);
200
    }
201
202
    /**
203
     * Uses _bulk to send documents to the server.
204
     *
205
     * @param objects[] $objects
206
     *
207
     * @return \Elastica\Bulk\ResponseSet
208
     *
209
     * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
210
     */
211
    public function addObjects(array $objects)
212
    {
213
        if (!isset($this->_serializer)) {
214
            throw new RuntimeException('No serializer defined');
215
        }
216
217
        $docs = [];
218
        foreach ($objects as $object) {
219
            $data = call_user_func($this->_serializer, $object);
220
            $doc = new Document();
221
            $doc->setData($data);
222
            $doc->setType($this->getName());
223
            $docs[] = $doc;
224
        }
225
226
        return $this->getIndex()->addDocuments($docs);
227
    }
228
229
    /**
230
     * Get the document from search index.
231
     *
232
     * @param string $id      Document id
233
     * @param array  $options Options for the get request.
234
     *
235
     * @throws \Elastica\Exception\NotFoundException
236
     * @throws \Elastica\Exception\ResponseException
237
     *
238
     * @return \Elastica\Document
239
     */
240
    public function getDocument($id, $options = [])
241
    {
242
        $endpoint = new \Elasticsearch\Endpoints\Get();
243
        $endpoint->setID($id);
244
        $endpoint->setParams($options);
245
246
        $response = $this->requestEndpoint($endpoint);
247
        $result = $response->getData();
248
249
        if (!isset($result['found']) || $result['found'] === false) {
250
            throw new NotFoundException('doc id '.$id.' not found');
251
        }
252
253
        if (isset($result['fields'])) {
254
            $data = $result['fields'];
255
        } elseif (isset($result['_source'])) {
256
            $data = $result['_source'];
257
        } else {
258
            $data = [];
259
        }
260
261
        $document = new Document($id, $data, $this->getName(), $this->getIndex());
262
        $document->setVersion($result['_version']);
263
264
        return $document;
265
    }
266
267
    /**
268
     * @param string       $id
269
     * @param array|string $data
270
     *
271
     * @return Document
272
     */
273
    public function createDocument($id = '', $data = [])
274
    {
275
        $document = new Document($id, $data);
276
        $document->setType($this);
277
278
        return $document;
279
    }
280
281
    /**
282
     * Returns the type name.
283
     *
284
     * @return string Type name
285
     */
286
    public function getName()
287
    {
288
        return $this->_name;
289
    }
290
291
    /**
292
     * Sets value type mapping for this type.
293
     *
294
     * @param \Elastica\Type\Mapping|array $mapping Elastica\Type\MappingType object or property array with all mappings
295
     *
296
     * @return \Elastica\Response
297
     */
298
    public function setMapping($mapping)
299
    {
300
        $mapping = Mapping::create($mapping);
301
        $mapping->setType($this);
302
303
        return $mapping->send();
304
    }
305
306
    /**
307
     * Returns current mapping for the given type.
308
     *
309
     * @return array Current mapping
310
     */
311 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...
312
    {
313
        $response = $this->requestEndpoint(new Get());
314
        $data = $response->getData();
315
316
        $mapping = array_shift($data);
317
        if (isset($mapping['mappings'])) {
318
            return $mapping['mappings'];
319
        }
320
321
        return [];
322
    }
323
324
    /**
325
     * Create search object.
326
     *
327
     * @param string|array|\Elastica\Query $query   Array with all query data inside or a Elastica\Query object
328
     * @param int|array                    $options OPTIONAL Limit or associative array of options (option=>value)
329
     * @param BuilderInterface             $builder
330
     *
331
     * @return Search
332
     */
333
    public function createSearch($query = '', $options = null, BuilderInterface $builder = null)
334
    {
335
        $search = $this->getIndex()->createSearch($query, $options, $builder);
336
        $search->addType($this);
337
338
        return $search;
339
    }
340
341
    /**
342
     * Do a search on this type.
343
     *
344
     * @param string|array|\Elastica\Query $query   Array with all query data inside or a Elastica\Query object
345
     * @param int|array                    $options OPTIONAL Limit or associative array of options (option=>value)
346
     *
347
     * @return \Elastica\ResultSet with all results inside
348
     *
349
     * @see \Elastica\SearchableInterface::search
350
     */
351
    public function search($query = '', $options = null)
352
    {
353
        $search = $this->createSearch($query, $options);
354
355
        return $search->search();
356
    }
357
358
    /**
359
     * Count docs by query.
360
     *
361
     * @param string|array|\Elastica\Query $query Array with all query data inside or a Elastica\Query object
362
     *
363
     * @return int number of documents matching the query
364
     *
365
     * @see \Elastica\SearchableInterface::count
366
     */
367
    public function count($query = '')
368
    {
369
        $search = $this->createSearch($query);
370
371
        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 371 which is incompatible with the return type declared by the interface Elastica\SearchableInterface::count of type integer.
Loading history...
372
    }
373
374
    /**
375
     * Returns index client.
376
     *
377
     * @return \Elastica\Index Index object
378
     */
379
    public function getIndex()
380
    {
381
        return $this->_index;
382
    }
383
384
    /**
385
     * @param \Elastica\Document $document
386
     *
387
     * @return \Elastica\Response
388
     */
389
    public function deleteDocument(Document $document)
390
    {
391
        $options = $document->getOptions(
392
            [
393
                'version',
394
                'version_type',
395
                'routing',
396
                'parent',
397
                'replication',
398
                'consistency',
399
                'refresh',
400
                'timeout',
401
            ]
402
        );
403
404
        return $this->deleteById($document->getId(), $options);
405
    }
406
407
    /**
408
     * Uses _bulk to delete documents from the server.
409
     *
410
     * @param array|\Elastica\Document[] $docs Array of Elastica\Document
411
     *
412
     * @return \Elastica\Bulk\ResponseSet
413
     *
414
     * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
415
     */
416
    public function deleteDocuments(array $docs)
417
    {
418
        foreach ($docs as $doc) {
419
            $doc->setType($this->getName());
420
        }
421
422
        return $this->getIndex()->deleteDocuments($docs);
423
    }
424
425
    /**
426
     * Deletes an entry by its unique identifier.
427
     *
428
     * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete.html
429
     *
430
     * @param int|string $id      Document id
431
     * @param array      $options
432
     *
433
     * @throws \InvalidArgumentException
434
     * @throws \Elastica\Exception\NotFoundException
435
     *
436
     * @return \Elastica\Response Response object
437
     */
438
    public function deleteById($id, array $options = [])
439
    {
440
        if (empty($id) || !trim($id)) {
441
            throw new \InvalidArgumentException();
442
        }
443
444
        $endpoint = new Delete();
445
        $endpoint->setID($id);
446
        $endpoint->setParams($options);
447
448
        $response = $this->requestEndpoint($endpoint);
449
450
        $responseData = $response->getData();
451
452
        if (isset($responseData['found']) && false == $responseData['found']) {
453
            throw new NotFoundException('Doc id '.$id.' not found and can not be deleted');
454
        }
455
456
        return $response;
457
    }
458
459
    /**
460
     * Deletes the given list of ids from this type.
461
     *
462
     * @param array       $ids
463
     * @param string|bool $routing Optional routing key for all ids
464
     *
465
     * @return \Elastica\Response Response  object
466
     */
467
    public function deleteIds(array $ids, $routing = false)
468
    {
469
        return $this->getIndex()->getClient()->deleteIds($ids, $this->getIndex(), $this, $routing);
470
    }
471
472
    /**
473
     * Deletes entries in the db based on a query.
474
     *
475
     * @param \Elastica\Query|string $query   Query object
476
     * @param array                  $options Optional params
477
     *
478
     * @return \Elastica\Response
479
     *
480
     * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html
481
     */
482
    public function deleteByQuery($query, array $options = [])
483
    {
484
        $query = Query::create($query);
485
486
        $endpoint = new DeleteByQuery();
487
        $endpoint->setBody($query->toArray());
488
        $endpoint->setParams($options);
489
490
        return $this->requestEndpoint($endpoint);
491
    }
492
493
    /**
494
     * Makes calls to the elasticsearch server based on this type.
495
     *
496
     * @param string $path   Path to call
497
     * @param string $method Rest method to use (GET, POST, DELETE, PUT)
498
     * @param array  $data   OPTIONAL Arguments as array
499
     * @param array  $query  OPTIONAL Query params
500
     *
501
     * @return \Elastica\Response Response object
502
     */
503
    public function request($path, $method, $data = [], array $query = [])
504
    {
505
        $path = $this->getName().'/'.$path;
506
507
        return $this->getIndex()->request($path, $method, $data, $query);
508
    }
509
510
    /**
511
     * Makes calls to the elasticsearch server with usage official client Endpoint based on this type
512
     *
513
     * @param AbstractEndpoint $endpoint
514
     * @return Response
515
     */
516
    public function requestEndpoint(AbstractEndpoint $endpoint)
517
    {
518
        $cloned = clone $endpoint;
519
        $cloned->setType($this->getName());
520
        return $this->getIndex()->requestEndpoint($cloned);
521
    }
522
523
    /**
524
     * Sets the serializer callable used in addObject.
525
     *
526
     * @see \Elastica\Type::addObject
527
     *
528
     * @param array|string $serializer @see \Elastica\Type::_serializer
529
     *
530
     * @return $this
531
     */
532
    public function setSerializer($serializer)
533
    {
534
        $this->_serializer = $serializer;
535
536
        return $this;
537
    }
538
539
    /**
540
     * Checks if the given type exists in Index.
541
     *
542
     * @return bool True if type exists
543
     */
544
    public function exists()
545
    {
546
        $response = $this->requestEndpoint(new Exists());
547
548
        return $response->getStatus() === 200;
549
    }
550
}
551