Completed
Pull Request — master (#1749)
by Gabriel
23:17 queued 21:08
created

SchemaManager::ensureDocumentIndexes()   C

Complexity

Conditions 8
Paths 5

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 8.0231

Importance

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