Completed
Push — upgrade ( f08820...3a8411 )
by Simonas
02:02
created

IndexService::getClient()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of the ONGR package.
5
 *
6
 * (c) NFQ Technologies UAB <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ONGR\ElasticsearchBundle\Service;
13
14
use Elasticsearch\Client;
15
use ONGR\ElasticsearchBundle\Event\BulkEvent;
16
use ONGR\ElasticsearchBundle\Event\CommitEvent;
17
use ONGR\ElasticsearchBundle\Event\Events;
18
use ONGR\ElasticsearchBundle\Exception\BulkWithErrorsException;
19
use ONGR\ElasticsearchBundle\Result\ArrayIterator;
20
use ONGR\ElasticsearchBundle\Result\Converter;
21
use ONGR\ElasticsearchBundle\Result\RawIterator;
22
use ONGR\ElasticsearchDSL\Query\FullText\QueryStringQuery;
23
use ONGR\ElasticsearchDSL\Query\Compound\BoolQuery;
24
use ONGR\ElasticsearchDSL\Query\TermLevel\IdsQuery;
25
use ONGR\ElasticsearchDSL\Search;
26
use ONGR\ElasticsearchDSL\Sort\FieldSort;
27
use ONGR\ElasticsearchBundle\Result\DocumentIterator;
28
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
29
30
/**
31
 * Document repository class.
32
 */
33
class IndexService
34
{
35
    private $client;
36
37
    private $indexName;
38
39
    private $converter;
40
41
    private $eventDispatcher;
42
43
    private $stopwatch;
44
45
    private $bulkCommitSize = 100;
46
47
    private $bulkQueries = [];
48
49
    private $commitMode = 'refresh';
50
51
    /**
52
     * @deprecated will be removed in v7 since there will be no more types in the indexes.
53
     */
54
    private $typeName;
55
56
    public function __construct(Client $client, Converter $converter, EventDispatcherInterface $eventDispatcher, array $mapping = [])
57
    {
58
        $this->client = $client;
59
        $this->typeName = $mapping['type'];
0 ignored issues
show
Deprecated Code introduced by
The property ONGR\ElasticsearchBundle...IndexService::$typeName has been deprecated with message: will be removed in v7 since there will be no more types in the indexes.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
60
        $this->converter = $converter;
61
        $this->eventDispatcher = $eventDispatcher;
62
    }
63
64
    /**
65
     * @deprecated will be removed in v7 since there will be no more types in the indexes.
66
     */
67
    public function getTypeName(): string
68
    {
69
        return $this->typeName;
0 ignored issues
show
Deprecated Code introduced by
The property ONGR\ElasticsearchBundle...IndexService::$typeName has been deprecated with message: will be removed in v7 since there will be no more types in the indexes.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
70
    }
71
72
    public function getClient(): Client
73
    {
74
        return $this->client;
75
    }
76
77
    public function getIndexName(): string
78
    {
79
        return $this->indexName;
80
    }
81
82
    public function getConverter(): Converter
83
    {
84
        return $this->converter;
85
    }
86
87
    public function getEventDispatcher(): EventDispatcherInterface
88
    {
89
        return $this->eventDispatcher;
90
    }
91
92
    public function getBulkCommitSize(): int
93
    {
94
        return $this->bulkCommitSize;
95
    }
96
97
    public function setBulkCommitSize(int $bulkCommitSize)
98
    {
99
        $this->bulkCommitSize = $bulkCommitSize;
100
        return $this;
101
    }
102
103
    public function getCommitMode(): string
104
    {
105
        return $this->commitMode;
106
    }
107
108
    public function setCommitMode(string $commitMode): IndexService
109
    {
110
        $this->commitMode = $commitMode;
111
        return $this;
112
    }
113
114
    public function getStopwatch()
115
    {
116
        return $this->stopwatch;
117
    }
118
119
    public function setStopwatch($stopwatch)
120
    {
121
        $this->stopwatch = $stopwatch;
122
        return $this;
123
    }
124
125
    public function createIndex($noMapping = false): array
0 ignored issues
show
Unused Code introduced by
The parameter $noMapping is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
126
    {
127
        return $this->getClient()->indices()->create($this->indexSettings);
0 ignored issues
show
Bug introduced by
The property indexSettings does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
128
    }
129
130
    public function dropIndex(): array
131
    {
132
        return $this->getClient()->indices()->delete(['index' => $this->getIndexName()]);
133
    }
134
135
    public function dropAndCreateIndex($noMapping = false)
136
    {
137
        try {
138
            if ($this->indexExists()) {
139
                $this->dropIndex();
140
            }
141
        } catch (\Exception $e) {
142
            // Do nothing, our target is to create new index.
143
        }
144
145
        return $this->createIndex($noMapping);
146
    }
147
148
    public function indexExists(): bool
149
    {
150
        return $this->getClient()->indices()->exists(['index' => $this->getIndexName()]);
151
    }
152
153
    /**
154
     * Returns a single document by provided ID or null if a document was not found.
155
     *
156
     * @param string $id      Document ID to find
157
     * @param array  $params  Custom parameters added to the query url
158
     *
159
     * @return object
160
     */
161
    public function find($id, $params = [])
162
    {
163
        $requestParams = [
164
            'index' => $this->getIndexName(),
165
            'type' => $this->getTypeName(),
0 ignored issues
show
Deprecated Code introduced by
The method ONGR\ElasticsearchBundle...xService::getTypeName() has been deprecated with message: will be removed in v7 since there will be no more types in the indexes.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
166
            'id' => $id,
167
        ];
168
169
        $requestParams = array_merge($requestParams, $params);
170
171
        $result = $this->getClient()->get($requestParams);
172
173
        return $this->getConverter()->convertToDocument($result, $this);
0 ignored issues
show
Documentation introduced by
$result is of type callable, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$this is of type this<ONGR\ElasticsearchB...e\Service\IndexService>, but the function expects a object<ONGR\ElasticsearchBundle\Service\Manager>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
174
    }
175
176
    public function findByIds(array $ids): DocumentIterator
177
    {
178
        $search = $this->createSearch();
179
        $search->addQuery(new IdsQuery($ids));
180
181
        return $this->findDocuments($search);
182
    }
183
184
    /**
185
     * Finds documents by a set of criteria.
186
     *
187
     * @param array      $criteria   Example: ['group' => ['best', 'worst'], 'job' => 'medic'].
188
     * @param array|null $orderBy    Example: ['name' => 'ASC', 'surname' => 'DESC'].
189
     * @param int|null   $limit      Default is 10.
190
     * @param int|null   $offset     Default is 0.
191
     *
192
     * @return array|DocumentIterator The objects.
193
     */
194
    public function findBy(
195
        array $criteria,
196
        array $orderBy = [],
197
        int $limit = 10,
198
        int $offset = 0
199
    ) {
200
        $search = $this->createSearch();
201
        $search->setSize($limit);
202
        $search->setFrom($offset);
203
204
        foreach ($criteria as $field => $value) {
205
            if (preg_match('/^!(.+)$/', $field)) {
206
                $boolType = BoolQuery::MUST_NOT;
207
                $field = preg_replace('/^!/', '', $field);
208
            } else {
209
                $boolType = BoolQuery::MUST;
210
            }
211
212
            $search->addQuery(
213
                new QueryStringQuery(is_array($value) ? implode(' OR ', $value) : $value, ['default_field' => $field]),
214
                $boolType
215
            );
216
        }
217
218
        foreach ($orderBy as $field => $direction) {
219
            $search->addSort(new FieldSort($field, $direction));
220
        }
221
222
        return $this->findDocuments($search);
223
    }
224
225
    /**
226
     * Finds a single document by a set of criteria.
227
     *
228
     * @param array      $criteria   Example: ['group' => ['best', 'worst'], 'job' => 'medic'].
229
     * @param array|null $orderBy    Example: ['name' => 'ASC', 'surname' => 'DESC'].
230
     *
231
     * @return object|null The object.
232
     */
233
    public function findOneBy(array $criteria, array $orderBy = [])
234
    {
235
        return $this->findBy($criteria, $orderBy, 1, null)->current();
236
    }
237
238
    public function createSearch(): Search
239
    {
240
        return new Search();
241
    }
242
243
    public function getScrollConfiguration($raw, $scrollDuration): array
244
    {
245
        $scrollConfig = [];
246
        if (isset($raw['_scroll_id'])) {
247
            $scrollConfig['_scroll_id'] = $raw['_scroll_id'];
248
            $scrollConfig['duration'] = $scrollDuration;
249
        }
250
251
        return $scrollConfig;
252
    }
253
254 View Code Duplication
    public function findDocuments(Search $search): DocumentIterator
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...
255
    {
256
        $results = $this->executeSearch($search);
257
258
        return new DocumentIterator(
259
            $results,
260
            $this->getManager(),
0 ignored issues
show
Bug introduced by
The method getManager() does not seem to exist on object<ONGR\Elasticsearc...e\Service\IndexService>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
261
            $this->getScrollConfiguration($results, $search->getScroll())
262
        );
263
    }
264
265 View Code Duplication
    public function findArray(Search $search): ArrayIterator
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...
266
    {
267
        $results = $this->executeSearch($search);
268
269
        return new ArrayIterator(
270
            $results,
271
            $this->getManager(),
0 ignored issues
show
Bug introduced by
The method getManager() does not seem to exist on object<ONGR\Elasticsearc...e\Service\IndexService>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
272
            $this->getScrollConfiguration($results, $search->getScroll())
273
        );
274
    }
275
276 View Code Duplication
    public function findRaw(Search $search): RawIterator
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...
277
    {
278
        $results = $this->executeSearch($search);
279
280
        return new RawIterator(
281
            $results,
282
            $this->getManager(),
0 ignored issues
show
Bug introduced by
The method getManager() does not seem to exist on object<ONGR\Elasticsearc...e\Service\IndexService>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
283
            $this->getScrollConfiguration($results, $search->getScroll())
284
        );
285
    }
286
287
    private function executeSearch(Search $search): array
288
    {
289
        return $this->search([$this->getTypeName()], $search->toArray(), $search->getUriParams());
0 ignored issues
show
Unused Code introduced by
The call to IndexService::search() has too many arguments starting with $search->getUriParams().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
Deprecated Code introduced by
The method ONGR\ElasticsearchBundle...xService::getTypeName() has been deprecated with message: will be removed in v7 since there will be no more types in the indexes.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
290
    }
291
292
    public function getIndexDocumentCount(): int
293
    {
294
        $body = [
295
            'index' => $this->getIndexName(),
296
            'type' => $this->getTypeName(),
0 ignored issues
show
Deprecated Code introduced by
The method ONGR\ElasticsearchBundle...xService::getTypeName() has been deprecated with message: will be removed in v7 since there will be no more types in the indexes.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
297
            'body' => [],
298
        ];
299
300
        $results = $this->getClient()->count($body);
301
302
        return $results['count'];
303
    }
304
305
    public function remove($id, $routing = null)
306
    {
307
        $params = [
308
            'index' => $this->getIndexName(),
309
            'type' => $this->getTypeName(),
0 ignored issues
show
Deprecated Code introduced by
The method ONGR\ElasticsearchBundle...xService::getTypeName() has been deprecated with message: will be removed in v7 since there will be no more types in the indexes.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
310
            'id' => $id,
311
        ];
312
313
        if ($routing) {
314
            $params['routing'] = $routing;
315
        }
316
317
        $response = $this->getClient()->delete($params);
318
319
        return $response;
320
    }
321
322
    public function update($id, array $fields = [], $script = null, array $params = []): array
323
    {
324
        $body = array_filter(
325
            [
326
                'doc' => $fields,
327
                'script' => $script,
328
            ]
329
        );
330
331
        $params = array_merge(
332
            [
333
                'id' => $id,
334
                'index' => $this->getIndexName(),
335
                'type' => $this->getTypeName(),
0 ignored issues
show
Deprecated Code introduced by
The method ONGR\ElasticsearchBundle...xService::getTypeName() has been deprecated with message: will be removed in v7 since there will be no more types in the indexes.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
336
                'body' => $body,
337
            ],
338
            $params
339
        );
340
341
        return $this->getClient()->update($params);
342
    }
343
344
    public function search(array $query, array $params = []): array
345
    {
346
        $requestParams = [
347
            'index' => $this->getIndexName(),
348
            'type' => $this->getTypeName(),
0 ignored issues
show
Deprecated Code introduced by
The method ONGR\ElasticsearchBundle...xService::getTypeName() has been deprecated with message: will be removed in v7 since there will be no more types in the indexes.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
349
            'body' => $query,
350
        ];
351
352
353
        if (!empty($params)) {
354
            $params = array_merge($requestParams, $params);
0 ignored issues
show
Unused Code introduced by
$params is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
355
        }
356
357
//        $this->stopwatch('start', 'search');
358
        $result = $this->client->search($requestParams);
359
//        $this->stopwatch('stop', 'search');
360
361
        return $result;
362
    }
363
364
    public function bulk(string $operation, array $header, array $query = []): array
365
    {
366
        $this->eventDispatcher->dispatch(
367
            Events::BULK,
368
            new BulkEvent($operation, $this->getTypeName(), $header, $query)
0 ignored issues
show
Deprecated Code introduced by
The method ONGR\ElasticsearchBundle...xService::getTypeName() has been deprecated with message: will be removed in v7 since there will be no more types in the indexes.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
369
        );
370
371
        $this->bulkQueries[] = $header;
372
373
        if (!empty($query)) $this->bulkQueries[] = $query;
374
375
        $response = [];
376
        // %2 is not very accurate, but better than use counter. This place is experimental for now.
377
        if ($this->getBulkCommitSize() >= count($this->bulkQueries % 2)) {
378
            $response = $this->commit();
379
        }
380
381
        return $response;
382
    }
383
384
    /**
385
     * Adds document to next flush.
386
     *
387
     * @param object $document
388
     */
389 View Code Duplication
    public function persist($document)
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...
390
    {
391
        $documentArray = $this->converter->convertToArray($document);
392
        $type = $this->getMetadataCollector()->getDocumentType(get_class($document));
0 ignored issues
show
Bug introduced by
The method getMetadataCollector() does not seem to exist on object<ONGR\Elasticsearc...e\Service\IndexService>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
393
394
        $this->bulk('index', $type, $documentArray);
395
    }
396
397
    public function commit(array $params = []): array
398
    {
399
        $bulkResponse = [];
400
        if (!empty($this->bulkQueries)) {
401
            $this->eventDispatcher->dispatch(
402
                Events::PRE_COMMIT,
403
                new CommitEvent($this->getCommitMode(), $this->bulkQueries)
404
            );
405
406
//            $this->stopwatch('start', 'bulk');
407
            $bulkResponse = $this->client->bulk(
408
                array_merge(
409
                    [
410
                    'index' => $this->getIndexName(),
411
                    'body' => $this->bulkQueries,
412
                    ],
413
                    $params
414
                )
415
            );
416
//            $this->stopwatch('stop', 'bulk');
417
418
            if ($bulkResponse['errors']) {
419
                throw new BulkWithErrorsException(
420
                    json_encode($bulkResponse),
421
                    0,
422
                    null,
423
                    $bulkResponse
0 ignored issues
show
Documentation introduced by
$bulkResponse is of type callable, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
424
                );
425
            }
426
427
            $this->stopwatch('start', 'refresh');
428
429
            switch ($this->getCommitMode()) {
430
                case 'flush':
431
                    $this->getClient()->indices()->flush();
432
                    break;
433
                case 'flush_synced':
434
                    $this->getClient()->indices()->flushSynced();
435
                    break;
436
                case 'refresh':
437
                    $this->getClient()->indices()->refresh();
438
                    break;
439
            }
440
441
            $this->eventDispatcher->dispatch(
442
                Events::POST_COMMIT,
443
                new CommitEvent($this->getCommitMode(), $this->bulkQueries, $bulkResponse)
0 ignored issues
show
Documentation introduced by
$bulkResponse is of type callable, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
444
            );
445
446
            $this->bulkQueries = [];
447
448
            $this->stopwatch('stop', $this->getCommitMode());
449
        }
450
451
        return $bulkResponse;
452
    }
453
454
    public function flush(array $params = []): array
455
    {
456
        return $this->client->indices()->flush(array_merge(['index' => $this->getIndexName()], $params));
457
    }
458
459
    public function refresh(array $params = []): array
460
    {
461
        return $this->client->indices()->refresh(array_merge(['index' => $this->getIndexName()], $params));
462
    }
463
464
    public function scroll($scrollId, $scrollDuration = '5m'): array
465
    {
466
        $results = $this->getClient()->scroll(['scroll_id' => $scrollId, 'scroll' => $scrollDuration]);
467
468
        return $results;
469
    }
470
471
    public function clearScroll($scrollId): array
472
    {
473
        return $this->getClient()->clearScroll(['scroll_id' => $scrollId]);
474
    }
475
476
    public function clearElasticIndexCache(): array
477
    {
478
        return $this->getClient()->indices()->clearCache(['index' => $this->getIndexName()]);
479
    }
480
481
    private function stopwatch($action, $name): void
482
    {
483
        if ($this->stopwatch && ($action == 'start' || $action == 'stop')) {
484
            $this->stopwatch->$action('ongr_es: '.$name, 'ongr_es');
485
        }
486
    }
487
}
488