Completed
Pull Request — master (#1887)
by
unknown
14:44 queued 07:29
created

SchemaManager::doGetDocumentIndexes()   C

Complexity

Conditions 15
Paths 7

Size

Total Lines 58

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 15.0062

Importance

Changes 0
Metric Value
dl 0
loc 58
ccs 32
cts 33
cp 0.9697
rs 5.9166
c 0
b 0
f 0
cc 15
nc 7
nop 2
crap 15.0062

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB;
6
7
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
8
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory;
9
use InvalidArgumentException;
10
use MongoDB\Driver\Exception\RuntimeException;
11
use MongoDB\Driver\Exception\ServerException;
12
use MongoDB\Model\IndexInfo;
13
use function array_filter;
14
use function array_unique;
15
use function iterator_count;
16
use function iterator_to_array;
17
use function ksort;
18
19
class SchemaManager
20
{
21
    private const GRIDFS_FILE_COLLECTION_INDEX = ['files_id' => 1, 'n' => 1];
22
23
    private const GRIDFS_CHUNKS_COLLECTION_INDEX = ['filename' => 1, 'uploadDate' => 1];
24
25
    private const CODE_SHARDING_ALREADY_INITIALIZED = 23;
26
27
    /** @var DocumentManager */
28
    protected $dm;
29
30
    /** @var ClassMetadataFactory */
31
    protected $metadataFactory;
32
33 1698
    public function __construct(DocumentManager $dm, ClassMetadataFactory $cmf)
34
    {
35 1698
        $this->dm              = $dm;
36 1698
        $this->metadataFactory = $cmf;
37 1698
    }
38
39
    /**
40
     * Ensure indexes are created for all documents that can be loaded with the
41
     * metadata factory.
42
     */
43 1
    public function ensureIndexes(?int $timeout = null) : void
44
    {
45 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
46 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
47 1
                continue;
48
            }
49
50 1
            $this->ensureDocumentIndexes($class->name, $timeout);
51
        }
52 1
    }
53
54
    /**
55
     * Ensure indexes exist for all mapped document classes.
56
     *
57
     * Indexes that exist in MongoDB but not the document metadata will be
58
     * deleted.
59
     */
60
    public function updateIndexes(?int $timeout = null) : void
61
    {
62
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
63
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
64
                continue;
65
            }
66
67
            $this->updateDocumentIndexes($class->name, $timeout);
68
        }
69
    }
70
71
    /**
72
     * Ensure indexes exist for the mapped document class.
73
     *
74
     * Indexes that exist in MongoDB but not the document metadata will be
75
     * deleted.
76
     *
77
     * @throws InvalidArgumentException
78
     */
79 3
    public function updateDocumentIndexes(string $documentName, ?int $timeout = null) : void
80
    {
81 3
        $class = $this->dm->getClassMetadata($documentName);
82
83 3
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
84
            throw new InvalidArgumentException('Cannot update document indexes for mapped super classes, embedded documents or aggregation result documents.');
85
        }
86
87 3
        $documentIndexes = $this->getDocumentIndexes($documentName);
88 3
        $collection      = $this->dm->getDocumentCollection($documentName);
89 3
        $mongoIndexes    = iterator_to_array($collection->listIndexes());
90
91
        /* Determine which Mongo indexes should be deleted. Exclude the ID index
92
         * and those that are equivalent to any in the class metadata.
93
         */
94 3
        $self         = $this;
95
        $mongoIndexes = array_filter($mongoIndexes, static function (IndexInfo $mongoIndex) use ($documentIndexes, $self) {
96 1
            if ($mongoIndex['name'] === '_id_') {
97
                return false;
98
            }
99
100 1
            foreach ($documentIndexes as $documentIndex) {
101 1
                if ($self->isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex)) {
102 1
                    return false;
103
                }
104
            }
105
106 1
            return true;
107 3
        });
108
109
        // Delete indexes that do not exist in class metadata
110 3
        foreach ($mongoIndexes as $mongoIndex) {
111 1
            if (! isset($mongoIndex['name'])) {
112
                continue;
113
            }
114
115 1
            $collection->dropIndex($mongoIndex['name']);
116
        }
117
118 3
        $this->ensureDocumentIndexes($documentName, $timeout);
119 3
    }
120
121 23
    public function getDocumentIndexes(string $documentName) : array
122
    {
123 23
        $visited = [];
124 23
        return $this->doGetDocumentIndexes($documentName, $visited);
125
    }
126
127 23
    private function doGetDocumentIndexes(string $documentName, array &$visited) : array
128
    {
129 23
        if (isset($visited[$documentName])) {
130 1
            return [];
131
        }
132
133 23
        $visited[$documentName] = true;
134
135 23
        $class                   = $this->dm->getClassMetadata($documentName);
136 23
        $indexes                 = $this->prepareIndexes($class);
137 23
        $embeddedDocumentIndexes = [];
138
139
        // Add indexes from embedded & referenced documents
140 23
        foreach ($class->fieldMappings as $fieldMapping) {
141 23
            if (isset($fieldMapping['embedded'])) {
142 4
                if (isset($fieldMapping['targetDocument'])) {
143 4
                    $possibleEmbeds = [$fieldMapping['targetDocument']];
144 2
                } elseif (isset($fieldMapping['discriminatorMap'])) {
145 2
                    $possibleEmbeds = array_unique($fieldMapping['discriminatorMap']);
146
                } else {
147
                    continue;
148
                }
149
150 4
                foreach ($possibleEmbeds as $embed) {
151 4
                    if (isset($embeddedDocumentIndexes[$embed])) {
152 2
                        $embeddedIndexes = $embeddedDocumentIndexes[$embed];
153
                    } else {
154 4
                        $embeddedIndexes                 = $this->doGetDocumentIndexes($embed, $visited);
155 4
                        $embeddedDocumentIndexes[$embed] = $embeddedIndexes;
156
                    }
157
158 4
                    foreach ($embeddedIndexes as $embeddedIndex) {
159 2
                        foreach ($embeddedIndex['keys'] as $key => $value) {
160 2
                            $embeddedIndex['keys'][$fieldMapping['name'] . '.' . $key] = $value;
161 2
                            unset($embeddedIndex['keys'][$key]);
162
                        }
163
164 4
                        $indexes[] = $embeddedIndex;
165
                    }
166
                }
167 23
            } elseif (isset($fieldMapping['reference']) && isset($fieldMapping['targetDocument'])) {
168 11
                foreach ($indexes as $idx => $index) {
169 10
                    $newKeys = [];
170 10
                    foreach ($index['keys'] as $key => $v) {
171 10
                        if ($key === $fieldMapping['name']) {
172 2
                            $key = ClassMetadata::getReferenceFieldName($fieldMapping['storeAs'], $key);
173
                        }
174
175 10
                        $newKeys[$key] = $v;
176
                    }
177
178 23
                    $indexes[$idx]['keys'] = $newKeys;
179
                }
180
            }
181
        }
182
183 23
        return $indexes;
184
    }
185
186 23
    private function prepareIndexes(ClassMetadata $class) : array
187
    {
188 23
        $persister  = $this->dm->getUnitOfWork()->getDocumentPersister($class->name);
189 23
        $indexes    = $class->getIndexes();
190 23
        $newIndexes = [];
191
192 23
        foreach ($indexes as $index) {
193
            $newIndex = [
194 22
                'keys' => [],
195 22
                'options' => $index['options'],
196
            ];
197
198 22
            foreach ($index['keys'] as $key => $value) {
199 22
                $key = $persister->prepareFieldName($key);
200 22
                if ($class->hasField($key)) {
201 17
                    $mapping                            = $class->getFieldMapping($key);
202 17
                    $newIndex['keys'][$mapping['name']] = $value;
203
                } else {
204 22
                    $newIndex['keys'][$key] = $value;
205
                }
206
            }
207
208 22
            $newIndexes[] = $newIndex;
209
        }
210
211 23
        return $newIndexes;
212
    }
213
214
    /**
215
     * Ensure the given document's indexes are created.
216
     *
217
     * @throws InvalidArgumentException
218
     */
219 19
    public function ensureDocumentIndexes(string $documentName, ?int $timeoutMs = null) : void
220
    {
221 19
        $class = $this->dm->getClassMetadata($documentName);
222 19
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
223
            throw new InvalidArgumentException('Cannot create document indexes for mapped super classes, embedded documents or query result documents.');
224
        }
225
226 19
        if ($class->isFile) {
227 2
            $this->ensureGridFSIndexes($class);
228
        }
229
230 19
        $indexes = $this->getDocumentIndexes($documentName);
231 19
        if (! $indexes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $indexes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
232 4
            return;
233
        }
234
235 18
        $collection = $this->dm->getDocumentCollection($class->name);
236 18
        foreach ($indexes as $index) {
237 18
            $keys    = $index['keys'];
238 18
            $options = $index['options'];
239
240 18
            if (! isset($options['timeout']) && isset($timeoutMs)) {
241 1
                $options['timeout'] = $timeoutMs;
242
            }
243
244 18
            $collection->createIndex($keys, $options);
245
        }
246 18
    }
247
248
    /**
249
     * Delete indexes for all documents that can be loaded with the
250
     * metadata factory.
251
     */
252 1
    public function deleteIndexes() : void
253
    {
254 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
255 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
256 1
                continue;
257
            }
258
259 1
            $this->deleteDocumentIndexes($class->name);
260
        }
261 1
    }
262
263
    /**
264
     * Delete the given document's indexes.
265
     *
266
     * @throws InvalidArgumentException
267
     */
268 2
    public function deleteDocumentIndexes(string $documentName) : void
269
    {
270 2
        $class = $this->dm->getClassMetadata($documentName);
271 2
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
272
            throw new InvalidArgumentException('Cannot delete document indexes for mapped super classes, embedded documents or query result documents.');
273
        }
274
275 2
        $this->dm->getDocumentCollection($documentName)->dropIndexes();
276 2
    }
277
278
    /**
279
     * Create all the mapped document collections in the metadata factory.
280
     */
281 1
    public function createCollections() : void
282
    {
283 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
284 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
285 1
                continue;
286
            }
287 1
            $this->createDocumentCollection($class->name);
288
        }
289 1
    }
290
291
    /**
292
     * Create the document collection for a mapped class.
293
     *
294
     * @throws InvalidArgumentException
295
     */
296 5
    public function createDocumentCollection(string $documentName) : void
297
    {
298 5
        $class = $this->dm->getClassMetadata($documentName);
299
300 5
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
301
            throw new InvalidArgumentException('Cannot create document collection for mapped super classes, embedded documents or query result documents.');
302
        }
303
304 5
        if ($class->isFile) {
305 2
            $this->dm->getDocumentDatabase($documentName)->createCollection($class->getBucketName() . '.files');
306 2
            $this->dm->getDocumentDatabase($documentName)->createCollection($class->getBucketName() . '.chunks');
307
308 2
            return;
309
        }
310
311 4
        $this->dm->getDocumentDatabase($documentName)->createCollection(
312 4
            $class->getCollection(),
313
            [
314 4
                'capped' => $class->getCollectionCapped(),
315 4
                'size' => $class->getCollectionSize(),
316 4
                'max' => $class->getCollectionMax(),
317
            ]
318
        );
319 4
    }
320
321
    /**
322
     * Drop all the mapped document collections in the metadata factory.
323
     */
324 1
    public function dropCollections() : void
325
    {
326 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
327 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
328 1
                continue;
329
            }
330
331 1
            $this->dropDocumentCollection($class->name);
332
        }
333 1
    }
334
335
    /**
336
     * Drop the document collection for a mapped class.
337
     *
338
     * @throws InvalidArgumentException
339
     */
340 5
    public function dropDocumentCollection(string $documentName) : void
341
    {
342 5
        $class = $this->dm->getClassMetadata($documentName);
343 5
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
344
            throw new InvalidArgumentException('Cannot delete document indexes for mapped super classes, embedded documents or query result documents.');
345
        }
346
347 5
        $this->dm->getDocumentCollection($documentName)->drop();
348
349 5
        if (! $class->isFile) {
350 4
            return;
351
        }
352
353 2
        $this->dm->getDocumentBucket($documentName)->getChunksCollection()->drop();
354 2
    }
355
356
    /**
357
     * Drop all the mapped document databases in the metadata factory.
358
     */
359 1
    public function dropDatabases() : void
360
    {
361 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
362 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
363 1
                continue;
364
            }
365
366 1
            $this->dropDocumentDatabase($class->name);
367
        }
368 1
    }
369
370
    /**
371
     * Drop the document database for a mapped class.
372
     *
373
     * @throws InvalidArgumentException
374
     */
375 2
    public function dropDocumentDatabase(string $documentName) : void
376
    {
377 2
        $class = $this->dm->getClassMetadata($documentName);
378 2
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
379
            throw new InvalidArgumentException('Cannot drop document database for mapped super classes, embedded documents or query result documents.');
380
        }
381
382 2
        $this->dm->getDocumentDatabase($documentName)->drop();
383 2
    }
384
385
    /**
386
     * Determine if an index returned by MongoCollection::getIndexInfo() can be
387
     * considered equivalent to an index in class metadata.
388
     *
389
     * Indexes are considered different if:
390
     *
391
     *   (a) Key/direction pairs differ or are not in the same order
392
     *   (b) Sparse or unique options differ
393
     *   (c) Mongo index is unique without dropDups and mapped index is unique
394
     *       with dropDups
395
     *   (d) Geospatial options differ (bits, max, min)
396
     *   (e) The partialFilterExpression differs
397
     *
398
     * Regarding (c), the inverse case is not a reason to delete and
399
     * recreate the index, since dropDups only affects creation of
400
     * the unique index. Additionally, the background option is only
401
     * relevant to index creation and is not considered.
402
     *
403
     * @param array|IndexInfo $mongoIndex Mongo index data.
404
     */
405 47
    public function isMongoIndexEquivalentToDocumentIndex($mongoIndex, array $documentIndex) : bool
406
    {
407 47
        $documentIndexOptions = $documentIndex['options'];
408
409 47
        if (! $this->isEquivalentIndexKeys($mongoIndex, $documentIndex)) {
410 4
            return false;
411
        }
412
413 43
        if (empty($mongoIndex['sparse']) xor empty($documentIndexOptions['sparse'])) {
414 2
            return false;
415
        }
416
417 41
        if (empty($mongoIndex['unique']) xor empty($documentIndexOptions['unique'])) {
418 2
            return false;
419
        }
420
421 39
        if (! empty($mongoIndex['unique']) && empty($mongoIndex['dropDups']) &&
422 39
            ! empty($documentIndexOptions['unique']) && ! empty($documentIndexOptions['dropDups'])) {
423 1
            return false;
424
        }
425
426 38
        foreach (['bits', 'max', 'min'] as $option) {
427 38
            if (isset($mongoIndex[$option]) xor isset($documentIndexOptions[$option])) {
428 6
                return false;
429
            }
430
431 36
            if (isset($mongoIndex[$option], $documentIndexOptions[$option]) &&
432 36
                $mongoIndex[$option] !== $documentIndexOptions[$option]) {
433 36
                return false;
434
            }
435
        }
436
437 29
        if (empty($mongoIndex['partialFilterExpression']) xor empty($documentIndexOptions['partialFilterExpression'])) {
438 2
            return false;
439
        }
440
441 27
        if (isset($mongoIndex['partialFilterExpression'], $documentIndexOptions['partialFilterExpression']) &&
442 27
            $mongoIndex['partialFilterExpression'] !== $documentIndexOptions['partialFilterExpression']) {
443 1
            return false;
444
        }
445
446 26
        if (isset($mongoIndex['weights']) && ! $this->isEquivalentTextIndexWeights($mongoIndex, $documentIndex)) {
447 2
            return false;
448
        }
449
450 24
        foreach (['default_language', 'language_override', 'textIndexVersion'] as $option) {
451
            /* Text indexes will always report defaults for these options, so
452
             * only compare if we have explicit values in the document index. */
453 24
            if (isset($mongoIndex[$option], $documentIndexOptions[$option]) &&
454 24
                $mongoIndex[$option] !== $documentIndexOptions[$option]) {
455 24
                return false;
456
            }
457
        }
458
459 21
        return true;
460
    }
461
462
    /**
463
     * Determine if the keys for a MongoDB index can be considered equivalent to
464
     * those for an index in class metadata.
465
     *
466
     * @param array|IndexInfo $mongoIndex Mongo index data.
467
     */
468 47
    private function isEquivalentIndexKeys($mongoIndex, array $documentIndex) : bool
469
    {
470 47
        $mongoIndexKeys    = $mongoIndex['key'];
471 47
        $documentIndexKeys = $documentIndex['keys'];
472
473
        /* If we are dealing with text indexes, we need to unset internal fields
474
         * from the MongoDB index and filter out text fields from the document
475
         * index. This will leave only non-text fields, which we can compare as
476
         * normal. Any text fields in the document index will be compared later
477
         * with isEquivalentTextIndexWeights(). */
478 47
        if (isset($mongoIndexKeys['_fts']) && $mongoIndexKeys['_fts'] === 'text') {
479 15
            unset($mongoIndexKeys['_fts'], $mongoIndexKeys['_ftsx']);
480
481
            $documentIndexKeys = array_filter($documentIndexKeys, static function ($type) {
482 15
                return $type !== 'text';
483 15
            });
484
        }
485
486
        /* Avoid a strict equality check here. The numeric type returned by
487
         * MongoDB may differ from the document index without implying that the
488
         * indexes themselves are inequivalent. */
489
        // phpcs:disable SlevomatCodingStandard.ControlStructures.DisallowEqualOperators.DisallowedEqualOperator
490 47
        return $mongoIndexKeys == $documentIndexKeys;
491
    }
492
493
    /**
494
     * Determine if the text index weights for a MongoDB index can be considered
495
     * equivalent to those for an index in class metadata.
496
     *
497
     * @param array|IndexInfo $mongoIndex Mongo index data.
498
     */
499 14
    private function isEquivalentTextIndexWeights($mongoIndex, array $documentIndex) : bool
500
    {
501 14
        $mongoIndexWeights    = $mongoIndex['weights'];
502 14
        $documentIndexWeights = $documentIndex['options']['weights'] ?? [];
503
504
        // If not specified, assign a default weight for text fields
505 14
        foreach ($documentIndex['keys'] as $key => $type) {
506 14
            if ($type !== 'text' || isset($documentIndexWeights[$key])) {
507 5
                continue;
508
            }
509
510 9
            $documentIndexWeights[$key] = 1;
511
        }
512
513
        /* MongoDB returns the weights sorted by field name, but we'll sort both
514
         * arrays in case that is internal behavior not to be relied upon. */
515 14
        ksort($mongoIndexWeights);
516 14
        ksort($documentIndexWeights);
517
518
        /* Avoid a strict equality check here. The numeric type returned by
519
         * MongoDB may differ from the document index without implying that the
520
         * indexes themselves are inequivalent. */
521
        // phpcs:disable SlevomatCodingStandard.ControlStructures.DisallowEqualOperators.DisallowedEqualOperator
522 14
        return $mongoIndexWeights == $documentIndexWeights;
523
    }
524
525
    /**
526
     * Ensure collections are sharded for all documents that can be loaded with the
527
     * metadata factory.
528
     *
529
     * @throws MongoDBException
530
     */
531
    public function ensureSharding() : void
532
    {
533
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
534
            if ($class->isMappedSuperclass || ! $class->isSharded()) {
535
                continue;
536
            }
537
538
            $this->ensureDocumentSharding($class->name);
539
        }
540
    }
541
542
    /**
543
     * Ensure sharding for collection by document name.
544
     *
545
     * @throws MongoDBException
546
     */
547 11
    public function ensureDocumentSharding(string $documentName) : void
548
    {
549 11
        $class = $this->dm->getClassMetadata($documentName);
550 11
        if (! $class->isSharded()) {
551
            return;
552
        }
553
554 11
        if ($this->collectionIsSharded($documentName)) {
555 1
            return;
556
        }
557
558 11
        $this->enableShardingForDbByDocumentName($documentName);
559
560
        try {
561 11
            $this->runShardCollectionCommand($documentName);
562 1
        } catch (RuntimeException $e) {
0 ignored issues
show
Bug introduced by
The class MongoDB\Driver\Exception\RuntimeException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
563 1
            throw MongoDBException::failedToEnsureDocumentSharding($documentName, $e->getMessage());
564
        }
565 10
    }
566
567
    /**
568
     * Enable sharding for database which contains documents with given name.
569
     *
570
     * @throws MongoDBException
571
     */
572 11
    public function enableShardingForDbByDocumentName(string $documentName) : void
573
    {
574 11
        $dbName  = $this->dm->getDocumentDatabase($documentName)->getDatabaseName();
575 11
        $adminDb = $this->dm->getClient()->selectDatabase('admin');
576
577
        try {
578 11
            $adminDb->command(['enableSharding' => $dbName]);
579 10
        } catch (ServerException $e) {
0 ignored issues
show
Bug introduced by
The class MongoDB\Driver\Exception\ServerException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
580
            // Don't throw an exception if sharding is already enabled; there's just no other way to check this
581 10
            if ($e->getCode() !== self::CODE_SHARDING_ALREADY_INITIALIZED) {
582 10
                throw MongoDBException::failedToEnableSharding($dbName, $e->getMessage());
583
            }
584
        } catch (RuntimeException $e) {
0 ignored issues
show
Bug introduced by
The class MongoDB\Driver\Exception\RuntimeException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
585
            throw MongoDBException::failedToEnableSharding($dbName, $e->getMessage());
586
        }
587 11
    }
588
589 11
    private function runShardCollectionCommand(string $documentName) : array
590
    {
591 11
        $class    = $this->dm->getClassMetadata($documentName);
592 11
        $dbName   = $this->dm->getDocumentDatabase($documentName)->getDatabaseName();
593 11
        $shardKey = $class->getShardKey();
594 11
        $adminDb  = $this->dm->getClient()->selectDatabase('admin');
595
596 11
        return $adminDb->command(
597
            [
598 11
                'shardCollection' => $dbName . '.' . $class->getCollection(),
599 11
                'key'             => $shardKey['keys'],
600
            ]
601 10
        )->toArray()[0];
602
    }
603
604 2
    private function ensureGridFSIndexes(ClassMetadata $class) : void
605
    {
606 2
        $this->ensureChunksIndex($class);
607 2
        $this->ensureFilesIndex($class);
608 2
    }
609
610 2
    private function ensureChunksIndex(ClassMetadata $class) : void
611
    {
612 2
        $chunksCollection = $this->dm->getDocumentBucket($class->getName())->getChunksCollection();
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
613 2
        foreach ($chunksCollection->listIndexes() as $index) {
614
            if ($index->isUnique() && $index->getKey() === self::GRIDFS_FILE_COLLECTION_INDEX) {
615
                return;
616
            }
617
        }
618
619 2
        $chunksCollection->createIndex(self::GRIDFS_FILE_COLLECTION_INDEX, ['unique' => true]);
620 2
    }
621
622 2
    private function ensureFilesIndex(ClassMetadata $class) : void
623
    {
624 2
        $filesCollection = $this->dm->getDocumentCollection($class->getName());
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
625 2
        foreach ($filesCollection->listIndexes() as $index) {
626
            if ($index->getKey() === self::GRIDFS_CHUNKS_COLLECTION_INDEX) {
627
                return;
628
            }
629
        }
630
631 2
        $filesCollection->createIndex(self::GRIDFS_CHUNKS_COLLECTION_INDEX);
632 2
    }
633
634 11
    private function collectionIsSharded(string $documentName) : bool
635
    {
636 11
        $class = $this->dm->getClassMetadata($documentName);
637
638 11
        $database    = $this->dm->getDocumentDatabase($documentName);
639 11
        $collections = $database->listCollections(['filter' => ['name' => $class->getCollection()]]);
640 11
        if (! iterator_count($collections)) {
641 7
            return false;
642
        }
643
644 4
        $stats = $database->command(['collstats' => $class->getCollection()])->toArray()[0];
645 4
        return (bool) ($stats['sharded'] ?? false);
646
    }
647
}
648