Completed
Push — master ( 8a6a3b...86b2ed )
by Nicolas
03:28
created

Type::addDocument()   B

Complexity

Conditions 9
Paths 14

Size

Total Lines 47

Duplication

Lines 0
Ratio 0 %

Importance

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