Completed
Push — upgrade ( f08820 )
by Simonas
14:39
created

Manager::getAliases()   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 1
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 Elasticsearch\Common\Exceptions\Missing404Exception;
16
use ONGR\ElasticsearchBundle\Event\Events;
17
use ONGR\ElasticsearchBundle\Event\BulkEvent;
18
use ONGR\ElasticsearchBundle\Event\CommitEvent;
19
use ONGR\ElasticsearchBundle\Exception\BulkWithErrorsException;
20
use ONGR\ElasticsearchBundle\Mapping\MetadataCollector;
21
use ONGR\ElasticsearchBundle\Result\Converter;
22
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
23
use Symfony\Component\Stopwatch\Stopwatch;
24
25
/**
26
 * @deprecated it will be deleted in v7. Use `IndexService` service instead.
27
 *
28
 * Manager class.
29
 */
30
class Manager
31
{
32
    /**
33
     * @var string Manager name
34
     */
35
    private $name;
36
37
    /**
38
     * @var array Manager configuration
39
     */
40
    private $config = [];
41
42
    /**
43
     * @var Client
44
     */
45
    private $client;
46
47
    /**
48
     * @var Converter
49
     */
50
    private $converter;
51
52
    /**
53
     * @var array Container for bulk queries
54
     */
55
    private $bulkQueries = [];
56
57
    /**
58
     * @var array Holder for consistency, refresh and replication parameters
59
     */
60
    private $bulkParams = [];
61
62
    /**
63
     * @var array
64
     */
65
    private $indexSettings;
66
67
    /**
68
     * @var MetadataCollector
69
     */
70
    private $metadataCollector;
71
72
    /**
73
     * After commit to make data available the refresh or flush operation is needed
74
     * so one of those methods has to be defined, the default is refresh.
75
     *
76
     * @var string
77
     */
78
    private $commitMode = 'refresh';
79
80
    /**
81
     * The size that defines after how much document inserts call commit function.
82
     *
83
     * @var int
84
     */
85
    private $bulkCommitSize = 100;
86
87
    /**
88
     * Container to count how many documents was passed to the bulk query.
89
     *
90
     * @var int
91
     */
92
    private $bulkCount = 0;
93
94
    /**
95
     * @var Repository[] Repository local cache
96
     */
97
    private $repositories;
98
99
    /**
100
     * @var EventDispatcherInterface
101
     */
102
    private $eventDispatcher;
103
104
    /**
105
     * @var Stopwatch
106
     */
107
    private $stopwatch;
108
109
    /**
110
     * @param string            $name              Manager name
111
     * @param array             $config            Manager configuration
112
     * @param Client            $client
113
     * @param array             $indexSettings
114
     * @param MetadataCollector $metadataCollector
115
     * @param Converter         $converter
116
     */
117
    public function __construct(
118
        $name,
119
        array $config,
120
        $client,
121
        array $indexSettings,
122
        $metadataCollector,
123
        $converter
124
    ) {
125
        $this->name = $name;
126
        $this->config = $config;
127
        $this->client = $client;
128
        $this->indexSettings = $indexSettings;
129
        $this->metadataCollector = $metadataCollector;
130
        $this->converter = $converter;
131
    }
132
133
    /**
134
     * Returns Elasticsearch connection.
135
     *
136
     * @return Client
137
     */
138
    public function getClient()
139
    {
140
        return $this->client;
141
    }
142
143
    /**
144
     * @return string
145
     */
146
    public function getName()
147
    {
148
        return $this->name;
149
    }
150
151
    /**
152
     * @return array
153
     */
154
    public function getConfig()
155
    {
156
        return $this->config;
157
    }
158
159
    /**
160
     * @param EventDispatcherInterface $eventDispatcher
161
     */
162
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
163
    {
164
        $this->eventDispatcher = $eventDispatcher;
165
    }
166
167
    /**
168
     * @param Stopwatch $stopwatch
169
     */
170
    public function setStopwatch(Stopwatch $stopwatch)
171
    {
172
        $this->stopwatch = $stopwatch;
173
    }
174
175
    /**
176
     * Returns repository by document class.
177
     *
178
     * @param string $className FQCN or string in Bundle:Document format
179
     *
180
     * @return Repository
181
     */
182
    public function getRepository($className)
183
    {
184
        if (!is_string($className)) {
185
            throw new \InvalidArgumentException('Document class must be a string.');
186
        }
187
188
        $directory = null;
189
190
        if (strpos($className, ':')) {
191
            $bundle = explode(':', $className)[0];
192
193
            if (isset($this->config['mappings'][$bundle]['document_dir'])) {
194
                $directory = $this->config['mappings'][$bundle]['document_dir'];
195
            }
196
        }
197
198
        $namespace = $this->getMetadataCollector()->getClassName($className, $directory);
199
200
        if (isset($this->repositories[$namespace])) {
201
            return $this->repositories[$namespace];
202
        }
203
204
        $repository = $this->createRepository($namespace);
205
        $this->repositories[$namespace] = $repository;
206
207
        return $repository;
208
    }
209
210
    /**
211
     * @return MetadataCollector
212
     */
213
    public function getMetadataCollector()
214
    {
215
        return $this->metadataCollector;
216
    }
217
218
    /**
219
     * @return Converter
220
     */
221
    public function getConverter()
222
    {
223
        return $this->converter;
224
    }
225
226
    /**
227
     * @return string
228
     */
229
    public function getCommitMode()
230
    {
231
        return $this->commitMode;
232
    }
233
234
    /**
235
     * @param string $commitMode
236
     */
237
    public function setCommitMode($commitMode)
238
    {
239
        if ($commitMode === 'refresh' || $commitMode === 'flush' || $commitMode === 'none') {
240
            $this->commitMode = $commitMode;
241
        } else {
242
            throw new \LogicException('The commit method must be either refresh, flush or none.');
243
        }
244
    }
245
246
    /**
247
     * @return int
248
     */
249
    public function getBulkCommitSize()
250
    {
251
        return $this->bulkCommitSize;
252
    }
253
254
    /**
255
     * @param int $bulkCommitSize
256
     */
257
    public function setBulkCommitSize($bulkCommitSize)
258
    {
259
        $this->bulkCommitSize = $bulkCommitSize;
260
    }
261
262
    /**
263
     * Creates a repository.
264
     *
265
     * @param string $className
266
     *
267
     * @return Repository
268
     */
269
    private function createRepository($className)
270
    {
271
        return new Repository($this, $className);
0 ignored issues
show
Deprecated Code introduced by
The class ONGR\ElasticsearchBundle\Service\Repository has been deprecated with message: it will be deleted in v7. Use `IndexService` service instead. Document repository class.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

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

Loading history...
272
    }
273
274
    /**
275
     * Executes search query in the index.
276
     *
277
     * @param array $types             List of types to search in.
278
     * @param array $query             Query to execute.
279
     * @param array $queryStringParams Query parameters.
280
     *
281
     * @return array
282
     */
283
    public function search(array $types, array $query, array $queryStringParams = [])
284
    {
285
        $params = [];
286
        $params['index'] = $this->getIndexName();
287
288
        $resolvedTypes = [];
289
        foreach ($types as $type) {
290
            $resolvedTypes[] = $this->resolveTypeName($type);
291
        }
292
293
        if (!empty($resolvedTypes)) {
294
            $params['type'] = implode(',', $resolvedTypes);
295
        }
296
297
        $params['body'] = $query;
298
299
        if (!empty($queryStringParams)) {
300
            $params = array_merge($queryStringParams, $params);
301
        }
302
303
        $this->stopwatch('start', 'search');
304
        $result = $this->client->search($params);
305
        $this->stopwatch('stop', 'search');
306
307
        return $result;
308
    }
309
310
    /**
311
     * Execute search queries using multisearch api
312
     * $body - is array of requests described in elastic Multi Search API
313
     *
314
     * @param $body
315
     * @return array
316
     */
317
    public function msearch(array $body)
318
    {
319
        $result = $this->client->msearch(
320
            [
321
                'index' => $this->getIndexName(), // set default index
322
                'body' => $body
323
            ]
324
        );
325
        return $result;
326
    }
327
328
    /**
329
     * Adds document to next flush.
330
     *
331
     * @param object $document
332
     */
333
    public function persist($document)
334
    {
335
        $documentArray = $this->converter->convertToArray($document);
336
        $type = $this->getMetadataCollector()->getDocumentType(get_class($document));
337
338
        $this->bulk('index', $type, $documentArray);
339
    }
340
341
    /**
342
     * Adds document for removal.
343
     *
344
     * @param object $document
345
     */
346
    public function remove($document)
347
    {
348
        $data = $this->converter->convertToArray($document, [], ['_id', '_routing']);
349
350
        if (!isset($data['_id'])) {
351
            throw new \LogicException(
352
                'In order to use remove() method document class must have property with @Id annotation.'
353
            );
354
        }
355
356
        $type = $this->getMetadataCollector()->getDocumentType(get_class($document));
357
358
        $this->bulk('delete', $type, $data);
359
    }
360
361
    /**
362
     * Flushes elasticsearch index.
363
     *
364
     * @param array $params
365
     *
366
     * @return array
367
     */
368
    public function flush(array $params = [])
369
    {
370
        return $this->client->indices()->flush(array_merge(['index' => $this->getIndexName()], $params));
371
    }
372
373
    /**
374
     * Refreshes elasticsearch index.
375
     *
376
     * @param array $params
377
     *
378
     * @return array
379
     */
380
    public function refresh(array $params = [])
381
    {
382
        return $this->client->indices()->refresh(array_merge(['index' => $this->getIndexName()], $params));
383
    }
384
385
    /**
386
     * Inserts the current query container to the index, used for bulk queries execution.
387
     *
388
     * @param array $params Parameters that will be passed to the flush or refresh queries.
389
     *
390
     * @return null|array
391
     *
392
     * @throws BulkWithErrorsException
393
     */
394
    public function commit(array $params = [])
395
    {
396
        if (!empty($this->bulkQueries)) {
397
            $bulkQueries = array_merge($this->bulkQueries, $this->bulkParams);
398
            $bulkQueries['index']['_index'] = $this->getIndexName();
399
            $this->eventDispatcher->dispatch(
400
                Events::PRE_COMMIT,
401
                new CommitEvent($this->getCommitMode(), $bulkQueries)
402
            );
403
404
            $this->stopwatch('start', 'bulk');
405
            $bulkResponse = $this->client->bulk($bulkQueries);
406
            $this->stopwatch('stop', 'bulk');
407
408
            if ($bulkResponse['errors']) {
409
                throw new BulkWithErrorsException(
410
                    json_encode($bulkResponse),
411
                    0,
412
                    null,
413
                    $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...
414
                );
415
            }
416
417
            $this->bulkQueries = [];
418
            $this->bulkCount = 0;
419
420
            $this->stopwatch('start', 'refresh');
421
422
            switch ($this->getCommitMode()) {
423
                case 'flush':
424
                    $this->flush($params);
425
                    break;
426
                case 'refresh':
427
                    $this->refresh($params);
428
                    break;
429
            }
430
431
            $this->eventDispatcher->dispatch(
432
                Events::POST_COMMIT,
433
                new CommitEvent($this->getCommitMode(), $bulkResponse)
0 ignored issues
show
Documentation introduced by
$bulkResponse is of type callable, but the function expects a array|null.

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...
434
            );
435
436
            $this->stopwatch('stop', 'refresh');
437
438
            return $bulkResponse;
439
        }
440
441
        return null;
442
    }
443
444
    /**
445
     * Adds query to bulk queries container.
446
     *
447
     * @param string       $operation One of: index, update, delete, create.
448
     * @param string|array $type      Elasticsearch type name.
449
     * @param array        $query     DSL to execute.
450
     *
451
     * @throws \InvalidArgumentException
452
     *
453
     * @return null|array
454
     */
455
    public function bulk($operation, $type, array $query)
456
    {
457
        if (!in_array($operation, ['index', 'create', 'update', 'delete'])) {
458
            throw new \InvalidArgumentException('Wrong bulk operation selected');
459
        }
460
461
        $this->eventDispatcher->dispatch(
462
            Events::BULK,
463
            new BulkEvent($operation, $type, $query)
464
        );
465
466
        $this->bulkQueries['body'][] = [
467
            $operation => array_filter(
468
                [
469
                    '_type' => $type,
470
                    '_id' => isset($query['_id']) ? $query['_id'] : null,
471
                    '_ttl' => isset($query['_ttl']) ? $query['_ttl'] : null,
472
                    '_routing' => isset($query['_routing']) ? $query['_routing'] : null,
473
                    '_parent' => isset($query['_parent']) ? $query['_parent'] : null,
474
                ]
475
            ),
476
        ];
477
        unset($query['_id'], $query['_ttl'], $query['_parent'], $query['_routing']);
478
479
        switch ($operation) {
480
            case 'index':
481
            case 'create':
482
            case 'update':
483
                $this->bulkQueries['body'][] = $query;
484
                break;
485
            case 'delete':
486
                // Body for delete operation is not needed to apply.
487
            default:
488
                // Do nothing.
489
                break;
490
        }
491
492
        // We are using counter because there is to difficult to resolve this from bulkQueries array.
493
        $this->bulkCount++;
494
495
        $response = null;
496
497
        if ($this->bulkCommitSize === $this->bulkCount) {
498
            $response = $this->commit();
499
        }
500
501
        return $response;
502
    }
503
504
    /**
505
     * Optional setter to change bulk query params.
506
     *
507
     * @param array $params Possible keys:
508
     *                      ['consistency'] = (enum) Explicit write consistency setting for the operation.
509
     *                      ['refresh']     = (boolean) Refresh the index after performing the operation.
510
     *                      ['replication'] = (enum) Explicitly set the replication type.
511
     */
512
    public function setBulkParams(array $params)
513
    {
514
        $this->bulkParams = $params;
515
    }
516
517
    /**
518
     * Creates fresh elasticsearch index.
519
     *
520
     * @param bool $noMapping Determines if mapping should be included.
521
     *
522
     * @return array
523
     */
524
    public function createIndex($noMapping = false)
525
    {
526
        if ($noMapping) {
527
            unset($this->indexSettings['body']['mappings']);
528
        }
529
530
        return $this->getClient()->indices()->create($this->indexSettings);
531
    }
532
533
    /**
534
     * Drops elasticsearch index.
535
     */
536
    public function dropIndex()
537
    {
538
        return $this->getClient()->indices()->delete(['index' => $this->getIndexName()]);
539
    }
540
541
    /**
542
     * Tries to drop and create fresh elasticsearch index.
543
     *
544
     * @param bool $noMapping Determines if mapping should be included.
545
     *
546
     * @return array
547
     */
548
    public function dropAndCreateIndex($noMapping = false)
549
    {
550
        try {
551
            if ($this->indexExists()) {
552
                $this->dropIndex();
553
            }
554
        } catch (\Exception $e) {
555
            // Do nothing, our target is to create new index.
556
        }
557
558
        return $this->createIndex($noMapping);
559
    }
560
561
    /**
562
     * Checks if connection index is already created.
563
     *
564
     * @return bool
565
     */
566
    public function indexExists()
567
    {
568
        return $this->getClient()->indices()->exists(['index' => $this->getIndexName()]);
569
    }
570
571
    /**
572
     * Returns index name this connection is attached to.
573
     *
574
     * @return string
575
     */
576
    public function getIndexName()
577
    {
578
        return $this->indexSettings['index'];
579
    }
580
581
    /**
582
     * Sets index name for this connection.
583
     *
584
     * @param string $name
585
     */
586
    public function setIndexName($name)
587
    {
588
        $this->indexSettings['index'] = $name;
589
    }
590
591
    /**
592
     * Returns mappings of the index for this connection.
593
     *
594
     * @return array
595
     */
596
    public function getIndexMappings()
597
    {
598
        return $this->indexSettings['body']['mappings'];
599
    }
600
601
    /**
602
     * Returns Elasticsearch version number.
603
     *
604
     * @return string
605
     */
606
    public function getVersionNumber()
607
    {
608
        return $this->client->info()['version']['number'];
609
    }
610
611
    /**
612
     * Clears elasticsearch client cache.
613
     */
614
    public function clearCache()
615
    {
616
        $this->getClient()->indices()->clearCache(['index' => $this->getIndexName()]);
617
    }
618
619
    /**
620
     * Returns a single document by ID. Returns NULL if document was not found.
621
     *
622
     * @param string $className Document class name or Elasticsearch type name
623
     * @param string $id        Document ID to find
624
     * @param string $routing   Custom routing for the document
625
     *
626
     * @return object
627
     */
628
    public function find($className, $id, $routing = null)
629
    {
630
        $type = $this->resolveTypeName($className);
631
632
        $params = [
633
            'index' => $this->getIndexName(),
634
            'type' => $type,
635
            'id' => $id,
636
        ];
637
638
        if ($routing) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $routing of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
639
            $params['routing'] = $routing;
640
        }
641
642
        try {
643
            $result = $this->getClient()->get($params);
644
        } catch (Missing404Exception $e) {
645
            return null;
646
        }
647
648
        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...
649
    }
650
651
    /**
652
     * Fetches next set of results.
653
     *
654
     * @param string $scrollId
655
     * @param string $scrollDuration
656
     *
657
     * @return mixed
658
     *
659
     * @throws \Exception
660
     */
661
    public function scroll(
662
        $scrollId,
663
        $scrollDuration = '5m'
664
    ) {
665
        $results = $this->getClient()->scroll(['scroll_id' => $scrollId, 'scroll' => $scrollDuration]);
666
667
        return $results;
668
    }
669
670
    /**
671
     * Clears scroll.
672
     *
673
     * @param string $scrollId
674
     */
675
    public function clearScroll($scrollId)
676
    {
677
        $this->getClient()->clearScroll(['scroll_id' => $scrollId]);
678
    }
679
680
    /**
681
     * Calls "Get Settings API" in Elasticsearch and will return you the currently configured settings.
682
     *
683
     * return array
684
     */
685
    public function getSettings()
686
    {
687
        return $this->getClient()->indices()->getSettings(['index' => $this->getIndexName()]);
688
    }
689
690
    /**
691
     * Gets Elasticsearch aliases information.
692
     * @param $params
693
     *
694
     * @return array
695
     */
696
    public function getAliases($params = [])
697
    {
698
        return $this->getClient()->indices()->getAliases(array_merge(['index' => $this->getIndexName()], $params));
699
    }
700
701
    /**
702
     * Resolves type name by class name.
703
     *
704
     * @param string $className
705
     *
706
     * @return string
707
     */
708
    private function resolveTypeName($className)
709
    {
710
        if (strpos($className, ':') !== false || strpos($className, '\\') !== false) {
711
            return $this->getMetadataCollector()->getDocumentType($className);
712
        }
713
714
        return $className;
715
    }
716
717
    /**
718
     * Starts and stops an event in the stopwatch
719
     *
720
     * @param string $action   only 'start' and 'stop'
721
     * @param string $name     name of the event
722
     */
723
    private function stopwatch($action, $name)
724
    {
725
        if (isset($this->stopwatch)) {
726
            $this->stopwatch->$action('ongr_es: '.$name, 'ongr_es');
727
        }
728
    }
729
}
730