Completed
Pull Request — master (#1790)
by Andreas
17:42
created

isMongoIndexEquivalentToDocumentIndex()   D

Complexity

Conditions 17
Paths 12

Size

Total Lines 43
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 17

Importance

Changes 0
Metric Value
dl 0
loc 43
ccs 23
cts 23
cp 1
rs 4.9807
c 0
b 0
f 0
cc 17
eloc 23
nc 12
nop 2
crap 17

How to fix   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 MongoDB\Driver\Exception\RuntimeException;
10
use MongoDB\Model\IndexInfo;
11
use function array_filter;
12
use function array_unique;
13
use function iterator_to_array;
14
use function strpos;
15
16
class SchemaManager
17
{
18
    /** @var DocumentManager */
19
    protected $dm;
20
21
    /** @var ClassMetadataFactory */
22
    protected $metadataFactory;
23
24 1637
    public function __construct(DocumentManager $dm, ClassMetadataFactory $cmf)
25
    {
26 1637
        $this->dm = $dm;
27 1637
        $this->metadataFactory = $cmf;
28 1637
    }
29
30
    /**
31
     * Ensure indexes are created for all documents that can be loaded with the
32
     * metadata factory.
33
     *
34
     * @param int $timeout Timeout (ms) for acknowledged index creation
35
     */
36 1 View Code Duplication
    public function ensureIndexes($timeout = null)
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...
37
    {
38 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
39 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
40 1
                continue;
41
            }
42
43 1
            $this->ensureDocumentIndexes($class->name, $timeout);
44
        }
45 1
    }
46
47
    /**
48
     * Ensure indexes exist for all mapped document classes.
49
     *
50
     * Indexes that exist in MongoDB but not the document metadata will be
51
     * deleted.
52
     *
53
     * @param int $timeout Timeout (ms) for acknowledged index creation
54
     */
55 View Code Duplication
    public function updateIndexes($timeout = null)
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...
56
    {
57
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
58
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
59
                continue;
60
            }
61
62
            $this->updateDocumentIndexes($class->name, $timeout);
63
        }
64
    }
65
66
    /**
67
     * Ensure indexes exist for the mapped document class.
68
     *
69
     * Indexes that exist in MongoDB but not the document metadata will be
70
     * deleted.
71
     *
72
     * @param string $documentName
73
     * @param int    $timeout      Timeout (ms) for acknowledged index creation
74
     * @throws \InvalidArgumentException
75
     */
76 3
    public function updateDocumentIndexes($documentName, $timeout = null)
77
    {
78 3
        $class = $this->dm->getClassMetadata($documentName);
79
80 3
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
81
            throw new \InvalidArgumentException('Cannot update document indexes for mapped super classes, embedded documents or aggregation result documents.');
82
        }
83
84 3
        $documentIndexes = $this->getDocumentIndexes($documentName);
85 3
        $collection = $this->dm->getDocumentCollection($documentName);
86 3
        $mongoIndexes = iterator_to_array($collection->listIndexes());
87
88
        /* Determine which Mongo indexes should be deleted. Exclude the ID index
89
         * and those that are equivalent to any in the class metadata.
90
         */
91 3
        $self = $this;
92
        $mongoIndexes = array_filter($mongoIndexes, function (IndexInfo $mongoIndex) use ($documentIndexes, $self) {
93 1
            if ($mongoIndex['name'] === '_id_') {
94
                return false;
95
            }
96
97 1
            foreach ($documentIndexes as $documentIndex) {
98 1
                if ($self->isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex)) {
99 1
                    return false;
100
                }
101
            }
102
103 1
            return true;
104 3
        });
105
106
        // Delete indexes that do not exist in class metadata
107 3
        foreach ($mongoIndexes as $mongoIndex) {
108 1
            if (! isset($mongoIndex['name'])) {
109
                continue;
110
            }
111
112 1
            $collection->dropIndex($mongoIndex['name']);
113
        }
114
115 3
        $this->ensureDocumentIndexes($documentName, $timeout);
116 3
    }
117
118
    /**
119
     * @param string $documentName
120
     * @return array
121
     */
122 20
    public function getDocumentIndexes($documentName)
123
    {
124 20
        $visited = [];
125 20
        return $this->doGetDocumentIndexes($documentName, $visited);
126
    }
127
128
    /**
129
     * @param string $documentName
130
     * @param array  $visited
131
     * @return array
132
     */
133 20
    private function doGetDocumentIndexes($documentName, array &$visited)
134
    {
135 20
        if (isset($visited[$documentName])) {
136 1
            return [];
137
        }
138
139 20
        $visited[$documentName] = true;
140
141 20
        $class = $this->dm->getClassMetadata($documentName);
142 20
        $indexes = $this->prepareIndexes($class);
143 20
        $embeddedDocumentIndexes = [];
144
145
        // Add indexes from embedded & referenced documents
146 20
        foreach ($class->fieldMappings as $fieldMapping) {
147 20
            if (isset($fieldMapping['embedded'])) {
148 4
                if (isset($fieldMapping['targetDocument'])) {
149 4
                    $possibleEmbeds = [$fieldMapping['targetDocument']];
150 2
                } elseif (isset($fieldMapping['discriminatorMap'])) {
151 1
                    $possibleEmbeds = array_unique($fieldMapping['discriminatorMap']);
152
                } else {
153 1
                    continue;
154
                }
155
156 4
                foreach ($possibleEmbeds as $embed) {
157 4
                    if (isset($embeddedDocumentIndexes[$embed])) {
158 2
                        $embeddedIndexes = $embeddedDocumentIndexes[$embed];
159
                    } else {
160 4
                        $embeddedIndexes = $this->doGetDocumentIndexes($embed, $visited);
161 4
                        $embeddedDocumentIndexes[$embed] = $embeddedIndexes;
162
                    }
163
164 4
                    foreach ($embeddedIndexes as $embeddedIndex) {
165 2
                        foreach ($embeddedIndex['keys'] as $key => $value) {
166 2
                            $embeddedIndex['keys'][$fieldMapping['name'] . '.' . $key] = $value;
167 2
                            unset($embeddedIndex['keys'][$key]);
168
                        }
169
170 4
                        $indexes[] = $embeddedIndex;
171
                    }
172
                }
173 20
            } elseif (isset($fieldMapping['reference']) && isset($fieldMapping['targetDocument'])) {
174 9
                foreach ($indexes as $idx => $index) {
175 8
                    $newKeys = [];
176 8
                    foreach ($index['keys'] as $key => $v) {
177 8
                        if ($key === $fieldMapping['name']) {
178 2
                            $key = ClassMetadata::getReferenceFieldName($fieldMapping['storeAs'], $key);
179
                        }
180
181 8
                        $newKeys[$key] = $v;
182
                    }
183
184 20
                    $indexes[$idx]['keys'] = $newKeys;
185
                }
186
            }
187
        }
188
189 20
        return $indexes;
190
    }
191
192
    /**
193
     * @return array
194
     */
195 20
    private function prepareIndexes(ClassMetadata $class)
196
    {
197 20
        $persister = $this->dm->getUnitOfWork()->getDocumentPersister($class->name);
198 20
        $indexes = $class->getIndexes();
199 20
        $newIndexes = [];
200
201 20
        foreach ($indexes as $index) {
202
            $newIndex = [
203 19
                'keys' => [],
204 19
                'options' => $index['options'],
205
            ];
206
207 19
            foreach ($index['keys'] as $key => $value) {
208 19
                $key = $persister->prepareFieldName($key);
209 19
                if ($class->hasField($key)) {
210 17
                    $mapping = $class->getFieldMapping($key);
211 17
                    $newIndex['keys'][$mapping['name']] = $value;
212
                } else {
213 19
                    $newIndex['keys'][$key] = $value;
214
                }
215
            }
216
217 19
            $newIndexes[] = $newIndex;
218
        }
219
220 20
        return $newIndexes;
221
    }
222
223
    /**
224
     * Ensure the given document's indexes are created.
225
     *
226
     * @param string $documentName
227
     * @param int    $timeout      Timeout (ms) for acknowledged index creation
228
     * @throws \InvalidArgumentException
229
     */
230 16
    public function ensureDocumentIndexes($documentName, $timeout = null)
231
    {
232 16
        $class = $this->dm->getClassMetadata($documentName);
233 16
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
234
            throw new \InvalidArgumentException('Cannot create document indexes for mapped super classes, embedded documents or query result documents.');
235
        }
236
237 16
        if ($class->isFile) {
238 2
            $this->ensureGridFSIndexes($class);
239
        }
240
241 16
        $indexes = $this->getDocumentIndexes($documentName);
242 16
        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...
243 4
            return;
244
        }
245
246 15
        $collection = $this->dm->getDocumentCollection($class->name);
247 15
        foreach ($indexes as $index) {
248 15
            $keys = $index['keys'];
249 15
            $options = $index['options'];
250
251 15
            if (! isset($options['timeout']) && isset($timeout)) {
252 1
                $options['timeout'] = $timeout;
253
            }
254
255 15
            $collection->createIndex($keys, $options);
256
        }
257 15
    }
258
259
    /**
260
     * Delete indexes for all documents that can be loaded with the
261
     * metadata factory.
262
     */
263 1 View Code Duplication
    public function deleteIndexes()
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...
264
    {
265 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
266 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
267 1
                continue;
268
            }
269
270 1
            $this->deleteDocumentIndexes($class->name);
271
        }
272 1
    }
273
274
    /**
275
     * Delete the given document's indexes.
276
     *
277
     * @param string $documentName
278
     * @throws \InvalidArgumentException
279
     */
280 2 View Code Duplication
    public function deleteDocumentIndexes($documentName)
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...
281
    {
282 2
        $class = $this->dm->getClassMetadata($documentName);
283 2
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
284
            throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes, embedded documents or query result documents.');
285
        }
286
287 2
        $this->dm->getDocumentCollection($documentName)->dropIndexes();
288 2
    }
289
290
    /**
291
     * Create all the mapped document collections in the metadata factory.
292
     */
293 1 View Code Duplication
    public function createCollections()
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...
294
    {
295 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
296 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
297 1
                continue;
298
            }
299 1
            $this->createDocumentCollection($class->name);
300
        }
301 1
    }
302
303
    /**
304
     * Create the document collection for a mapped class.
305
     *
306
     * @param string $documentName
307
     * @throws \InvalidArgumentException
308
     */
309 5
    public function createDocumentCollection($documentName)
310
    {
311 5
        $class = $this->dm->getClassMetadata($documentName);
312
313 5
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
314
            throw new \InvalidArgumentException('Cannot create document collection for mapped super classes, embedded documents or query result documents.');
315
        }
316
317 5
        if ($class->isFile) {
318 2
            $this->dm->getDocumentDatabase($documentName)->createCollection($class->getCollection() . '.files');
319 2
            $this->dm->getDocumentDatabase($documentName)->createCollection($class->getCollection() . '.chunks');
320
321 2
            return;
322
        }
323
324 4
        $this->dm->getDocumentDatabase($documentName)->createCollection(
325 4
            $class->getCollection(),
326
            [
327 4
                'capped' => $class->getCollectionCapped(),
328 4
                'size' => $class->getCollectionSize(),
329 4
                'max' => $class->getCollectionMax(),
330
            ]
331
        );
332 4
    }
333
334
    /**
335
     * Drop all the mapped document collections in the metadata factory.
336
     */
337 1 View Code Duplication
    public function dropCollections()
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...
338
    {
339 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
340 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
341 1
                continue;
342
            }
343
344 1
            $this->dropDocumentCollection($class->name);
345
        }
346 1
    }
347
348
    /**
349
     * Drop the document collection for a mapped class.
350
     *
351
     * @param string $documentName
352
     * @throws \InvalidArgumentException
353
     */
354 5
    public function dropDocumentCollection($documentName)
355
    {
356 5
        $class = $this->dm->getClassMetadata($documentName);
357 5
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
358
            throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes, embedded documents or query result documents.');
359
        }
360
361 5
        $this->dm->getDocumentCollection($documentName)->drop();
362
363 5
        if (! $class->isFile) {
364 4
            return;
365
        }
366
367 2
        $this->dm->getDocumentBucket($documentName)->getChunksCollection()->drop();
368 2
    }
369
370
    /**
371
     * Drop all the mapped document databases in the metadata factory.
372
     */
373 1 View Code Duplication
    public function dropDatabases()
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...
374
    {
375 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
376 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
377 1
                continue;
378
            }
379
380 1
            $this->dropDocumentDatabase($class->name);
381
        }
382 1
    }
383
384
    /**
385
     * Drop the document database for a mapped class.
386
     *
387
     * @param string $documentName
388
     * @throws \InvalidArgumentException
389
     */
390 2 View Code Duplication
    public function dropDocumentDatabase($documentName)
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...
391
    {
392 2
        $class = $this->dm->getClassMetadata($documentName);
393 2
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
394
            throw new \InvalidArgumentException('Cannot drop document database for mapped super classes, embedded documents or query result documents.');
395
        }
396
397 2
        $this->dm->getDocumentDatabase($documentName)->drop();
398 2
    }
399
400
    /**
401
     * Determine if an index returned by MongoCollection::getIndexInfo() can be
402
     * considered equivalent to an index in class metadata.
403
     *
404
     * Indexes are considered different if:
405
     *
406
     *   (a) Key/direction pairs differ or are not in the same order
407
     *   (b) Sparse or unique options differ
408
     *   (c) Mongo index is unique without dropDups and mapped index is unique
409
     *       with dropDups
410
     *   (d) Geospatial options differ (bits, max, min)
411
     *   (e) The partialFilterExpression differs
412
     *
413
     * Regarding (c), the inverse case is not a reason to delete and
414
     * recreate the index, since dropDups only affects creation of
415
     * the unique index. Additionally, the background option is only
416
     * relevant to index creation and is not considered.
417
     *
418
     * @param array|IndexInfo $mongoIndex    Mongo index data.
419
     * @param array           $documentIndex Document index data.
420
     * @return bool True if the indexes are equivalent, otherwise false.
421
     */
422 30
    public function isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex)
423
    {
424 30
        $documentIndexOptions = $documentIndex['options'];
425
426 30
        if ($mongoIndex['key'] !== $documentIndex['keys']) {
427 2
            return false;
428
        }
429
430 28
        if (empty($mongoIndex['sparse']) xor empty($documentIndexOptions['sparse'])) {
431 2
            return false;
432
        }
433
434 26
        if (empty($mongoIndex['unique']) xor empty($documentIndexOptions['unique'])) {
435 2
            return false;
436
        }
437
438 24
        if (! empty($mongoIndex['unique']) && empty($mongoIndex['dropDups']) &&
439 24
            ! empty($documentIndexOptions['unique']) && ! empty($documentIndexOptions['dropDups'])) {
440 1
            return false;
441
        }
442
443 23
        foreach (['bits', 'max', 'min'] as $option) {
444 23
            if (isset($mongoIndex[$option]) xor isset($documentIndexOptions[$option])) {
445 6
                return false;
446
            }
447
448 21
            if (isset($mongoIndex[$option]) && isset($documentIndexOptions[$option]) &&
449 21
                $mongoIndex[$option] !== $documentIndexOptions[$option]) {
450 21
                return false;
451
            }
452
        }
453
454 14
        if (empty($mongoIndex['partialFilterExpression']) xor empty($documentIndexOptions['partialFilterExpression'])) {
455 2
            return false;
456
        }
457
458 12
        if (isset($mongoIndex['partialFilterExpression']) && isset($documentIndexOptions['partialFilterExpression']) &&
459 12
            $mongoIndex['partialFilterExpression'] !== $documentIndexOptions['partialFilterExpression']) {
460 1
            return false;
461
        }
462
463 11
        return true;
464
    }
465
466
    /**
467
     * Ensure collections are sharded for all documents that can be loaded with the
468
     * metadata factory.
469
     *
470
     * @param array $indexOptions Options for `ensureIndex` command. It's performed on an existing collections
471
     *
472
     * @throws MongoDBException
473
     */
474
    public function ensureSharding(array $indexOptions = [])
475
    {
476
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
477
            if ($class->isMappedSuperclass || ! $class->isSharded()) {
478
                continue;
479
            }
480
481
            $this->ensureDocumentSharding($class->name, $indexOptions);
482
        }
483
    }
484
485
    /**
486
     * Ensure sharding for collection by document name.
487
     *
488
     * @param string $documentName
489
     * @param array  $indexOptions Options for `ensureIndex` command. It's performed on an existing collections.
490
     *
491
     * @throws MongoDBException
492
     */
493 2
    public function ensureDocumentSharding($documentName, array $indexOptions = [])
494
    {
495 2
        $class = $this->dm->getClassMetadata($documentName);
496 2
        if (! $class->isSharded()) {
497
            return;
498
        }
499
500 2
        $this->enableShardingForDbByDocumentName($documentName);
501
502 2
        $try = 0;
503
        do {
504
            try {
505 2
                $result = $this->runShardCollectionCommand($documentName);
506 2
                $done = true;
507
508
                // Need to check error message because MongoDB 3.0 does not return a code for this error
509 2
                if (! (bool) $result['ok'] && strpos($result['errmsg'], 'please create an index that starts') !== false) {
510
                    // The proposed key is not returned when using mongo-php-adapter with ext-mongodb.
511
                    // See https://github.com/mongodb/mongo-php-driver/issues/296 for details
512
                    $key = $result['proposedKey'] ?? $this->dm->getClassMetadata($documentName)->getShardKey()['keys'];
513
514
                    $this->dm->getDocumentCollection($documentName)->ensureIndex($key, $indexOptions);
0 ignored issues
show
Bug introduced by
The method ensureIndex() does not seem to exist on object<MongoDB\Collection>.

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...
515 2
                    $done = false;
516
                }
517 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...
518 1
                if ($e->getCode() === 20 || $e->getCode() === 23 || $e->getMessage() === 'already sharded') {
519 1
                    return;
520
                }
521
522
                throw $e;
523
            }
524 2
        } while (! $done && $try < 2);
0 ignored issues
show
introduced by
It seems like the condition $try < 2 is always satisfied by any possible value of $try. Are you sure you do not have a deadlock here?
Loading history...
525
526
        // Starting with MongoDB 3.2, this command returns code 20 when a collection is already sharded.
527
        // For older MongoDB versions, check the error message
528 2
        if ((bool) $result['ok'] || (isset($result['code']) && $result['code'] === 20) || $result['errmsg'] === 'already sharded') {
529 2
            return;
530
        }
531
532
        throw MongoDBException::failedToEnsureDocumentSharding($documentName, $result['errmsg']);
533
    }
534
535
    /**
536
     * Enable sharding for database which contains documents with given name.
537
     *
538
     * @param string $documentName
539
     *
540
     * @throws MongoDBException
541
     */
542 2
    public function enableShardingForDbByDocumentName($documentName)
543
    {
544 2
        $dbName = $this->dm->getDocumentDatabase($documentName)->getDatabaseName();
545 2
        $adminDb = $this->dm->getClient()->selectDatabase('admin');
546
547
        try {
548 2
            $adminDb->command(['enableSharding' => $dbName]);
549 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...
550 1
            if ($e->getCode() !== 23 || $e->getMessage() === 'already enabled') {
551
                throw MongoDBException::failedToEnableSharding($dbName, $e->getMessage());
552
            }
553
        }
554 2
    }
555
556
    /**
557
     * @param string $documentName
558
     *
559
     * @return array
560
     */
561 2
    private function runShardCollectionCommand($documentName)
562
    {
563 2
        $class = $this->dm->getClassMetadata($documentName);
564 2
        $dbName = $this->dm->getDocumentDatabase($documentName)->getDatabaseName();
565 2
        $shardKey = $class->getShardKey();
566 2
        $adminDb = $this->dm->getClient()->selectDatabase('admin');
567
568 2
        $result = $adminDb->command(
569
            [
570 2
                'shardCollection' => $dbName . '.' . $class->getCollection(),
571 2
                'key'             => $shardKey['keys'],
572
            ]
573 2
        )->toArray()[0];
574
575 2
        return $result;
576
    }
577
578 2
    private function ensureGridFSIndexes(ClassMetadata $class): void
579
    {
580 2
        $this->ensureChunksIndex($class);
581 2
        $this->ensureFilesIndex($class);
582 2
    }
583
584 2
    private function ensureChunksIndex(ClassMetadata $class): void
585
    {
586 2
        $chunksCollection = $this->dm->getDocumentBucket($class->getName())->getChunksCollection();
587 2
        foreach ($chunksCollection->listIndexes() as $index) {
588
            if ($index->isUnique() && $index->getKey() === ['files_id' => 1, 'n' => 1]) {
589
                return;
590
            }
591
        }
592
593 2
        $chunksCollection->createIndex(['files_id' => 1, 'n' => 1], ['unique' => true]);
594 2
    }
595
596 2
    private function ensureFilesIndex(ClassMetadata $class): void
597
    {
598 2
        $filesCollection = $this->dm->getDocumentCollection($class->getName());
599 2
        foreach ($filesCollection->listIndexes() as $index) {
600
            if ($index->getKey() === ['filename' => 1, 'uploadDate' => 1]) {
601
                return;
602
            }
603
        }
604
605 2
        $filesCollection->createIndex(['filename' => 1, 'uploadDate' => 1]);
606 2
    }
607
}
608