Completed
Pull Request — master (#1749)
by Gabriel
21:31
created

SchemaManager::ensureDocumentIndexes()   C

Complexity

Conditions 8
Paths 5

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 8.0368

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 11
cts 12
cp 0.9167
rs 6.6037
c 0
b 0
f 0
cc 8
eloc 13
nc 5
nop 2
crap 8.0368
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
    public function __construct(DocumentManager $dm, ClassMetadataFactory $cmf)
25
    {
26
        $this->dm = $dm;
27
        $this->metadataFactory = $cmf;
28
    }
29 1619
30
    /**
31 1619
     * Ensure indexes are created for all documents that can be loaded with the
32 1619
     * metadata factory.
33 1619
     *
34
     * @param int $timeout Timeout (ms) for acknowledged index creation
35
     */
36 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
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
39
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
40
                continue;
41 1
            }
42
            $this->ensureDocumentIndexes($class->name, $timeout);
43 1
        }
44 1
    }
45 1
46
    /**
47 1
     * Ensure indexes exist for all mapped document classes.
48
     *
49 1
     * 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
    public function updateDocumentIndexes($documentName, $timeout = null)
75
    {
76
        $class = $this->dm->getClassMetadata($documentName);
77
78
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
79 3
            throw new \InvalidArgumentException('Cannot update document indexes for mapped super classes, embedded documents or aggregation result documents.');
80
        }
81 3
82
        $documentIndexes = $this->getDocumentIndexes($documentName);
83 3
        $collection = $this->dm->getDocumentCollection($documentName);
84
        $mongoIndexes = iterator_to_array($collection->listIndexes());
85
86
        /* Determine which Mongo indexes should be deleted. Exclude the ID index
87 3
         * and those that are equivalent to any in the class metadata.
88 3
         */
89 3
        $self = $this;
90
        $mongoIndexes = array_filter($mongoIndexes, function (IndexInfo $mongoIndex) use ($documentIndexes, $self) {
91
            if ($mongoIndex['name'] === '_id_') {
92
                return false;
93
            }
94 3
95 3
            foreach ($documentIndexes as $documentIndex) {
96 1
                if ($self->isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex)) {
97
                    return false;
98
                }
99
            }
100 1
101 1
            return true;
102 1
        });
103
104
        // Delete indexes that do not exist in class metadata
105
        foreach ($mongoIndexes as $mongoIndex) {
106 1
            if (! isset($mongoIndex['name'])) {
107 3
                continue;
108
            }
109
110 3
            $collection->dropIndex($mongoIndex['name']);
111 1
        }
112
113
        $this->ensureDocumentIndexes($documentName, $timeout);
114
    }
115 1
116
    /**
117
     * @param string $documentName
118 3
     * @return array
119 3
     */
120
    public function getDocumentIndexes($documentName)
121
    {
122
        $visited = [];
123
        return $this->doGetDocumentIndexes($documentName, $visited);
124
    }
125 19
126
    /**
127 19
     * @param string $documentName
128 19
     * @param array  $visited
129
     * @return array
130
     */
131
    private function doGetDocumentIndexes($documentName, array &$visited)
132
    {
133
        if (isset($visited[$documentName])) {
134
            return [];
135
        }
136 19
137
        $visited[$documentName] = true;
138 19
139 1
        $class = $this->dm->getClassMetadata($documentName);
140
        $indexes = $this->prepareIndexes($class);
141
        $embeddedDocumentIndexes = [];
142 19
143
        // Add indexes from embedded & referenced documents
144 19
        foreach ($class->fieldMappings as $fieldMapping) {
145 19
            if (isset($fieldMapping['embedded'])) {
146 19
                if (isset($fieldMapping['targetDocument'])) {
147
                    $possibleEmbeds = [$fieldMapping['targetDocument']];
148
                } elseif (isset($fieldMapping['discriminatorMap'])) {
149 19
                    $possibleEmbeds = array_unique($fieldMapping['discriminatorMap']);
150 19
                } else {
151 3
                    continue;
152 3
                }
153 2
                foreach ($possibleEmbeds as $embed) {
154 1
                    if (isset($embeddedDocumentIndexes[$embed])) {
155
                        $embeddedIndexes = $embeddedDocumentIndexes[$embed];
156 1
                    } else {
157
                        $embeddedIndexes = $this->doGetDocumentIndexes($embed, $visited);
158 3
                        $embeddedDocumentIndexes[$embed] = $embeddedIndexes;
159 3
                    }
160 2
                    foreach ($embeddedIndexes as $embeddedIndex) {
161
                        foreach ($embeddedIndex['keys'] as $key => $value) {
162 3
                            $embeddedIndex['keys'][$fieldMapping['name'] . '.' . $key] = $value;
163 3
                            unset($embeddedIndex['keys'][$key]);
164
                        }
165 3
                        $indexes[] = $embeddedIndex;
166 2
                    }
167 2
                }
168 2
            } elseif (isset($fieldMapping['reference']) && isset($fieldMapping['targetDocument'])) {
169
                foreach ($indexes as $idx => $index) {
170 3
                    $newKeys = [];
171
                    foreach ($index['keys'] as $key => $v) {
172
                        if ($key === $fieldMapping['name']) {
173 19
                            $key = ClassMetadata::getReferenceFieldName($fieldMapping['storeAs'], $key);
174 8
                        }
175 8
                        $newKeys[$key] = $v;
176 8
                    }
177 8
                    $indexes[$idx]['keys'] = $newKeys;
178 2
                }
179
            }
180 8
        }
181
        return $indexes;
182 19
    }
183
184
    /**
185
     * @return array
186 19
     */
187
    private function prepareIndexes(ClassMetadata $class)
188
    {
189
        $persister = $this->dm->getUnitOfWork()->getDocumentPersister($class->name);
190
        $indexes = $class->getIndexes();
191
        $newIndexes = [];
192 19
193
        foreach ($indexes as $index) {
194 19
            $newIndex = [
195 19
                'keys' => [],
196 19
                'options' => $index['options'],
197
            ];
198 19
            foreach ($index['keys'] as $key => $value) {
199
                $key = $persister->prepareFieldName($key);
200 19
                if ($class->hasField($key)) {
201 19
                    $mapping = $class->getFieldMapping($key);
202
                    $newIndex['keys'][$mapping['name']] = $value;
203 19
                } else {
204 19
                    $newIndex['keys'][$key] = $value;
205 19
                }
206 17
            }
207 17
208
            $newIndexes[] = $newIndex;
209 19
        }
210
211
        return $newIndexes;
212
    }
213 19
214
    /**
215
     * Ensure the given document's indexes are created.
216 19
     *
217
     * @param string $documentName
218
     * @param int    $timeout      Timeout (ms) for acknowledged index creation
219
     * @throws \InvalidArgumentException
220
     */
221
    public function ensureDocumentIndexes($documentName, $timeout = null)
222
    {
223
        $class = $this->dm->getClassMetadata($documentName);
224
        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 15
        }
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
            $collection = $this->dm->getDocumentCollection($class->name);
231
            foreach ($indexes as $index) {
232 15
                $keys = $index['keys'];
233 15
                $options = $index['options'];
234 15
235 15
                if (! isset($options['timeout']) && isset($timeout)) {
236 15
                    $options['timeout'] = $timeout;
237
                }
238 15
239 1
                $collection->createIndex($keys, $options);
240
            }
241
        }
242 15
    }
243
244
    /**
245 15
     * Delete indexes for all documents that can be loaded with the
246
     * metadata factory.
247
     */
248 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
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
251 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
252
                continue;
253 1
            }
254 1
            $this->deleteDocumentIndexes($class->name);
255 1
        }
256
    }
257 1
258
    /**
259 1
     * Delete the given document's indexes.
260
     *
261
     * @param string $documentName
262
     * @throws \InvalidArgumentException
263
     */
264 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
        $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 2
        }
270 2
        $this->dm->getDocumentCollection($documentName)->dropIndexes();
271
    }
272
273 2
    /**
274 2
     * Create all the mapped document collections in the metadata factory.
275
     */
276 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
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
279 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
280
                continue;
281 1
            }
282 1
            $this->createDocumentCollection($class->name);
283 1
        }
284
    }
285 1
286
    /**
287 1
     * Create the document collection for a mapped class.
288
     *
289
     * @param string $documentName
290
     * @throws \InvalidArgumentException
291
     */
292
    public function createDocumentCollection($documentName)
293
    {
294
        $class = $this->dm->getClassMetadata($documentName);
295 4
296
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
297 4
            throw new \InvalidArgumentException('Cannot create document collection for mapped super classes, embedded documents or query result documents.');
298
        }
299 4
300
        $this->dm->getDocumentDatabase($documentName)->createCollection(
301
            $class->getCollection(),
302
            [
303 4
                'capped' => $class->getCollectionCapped(),
304 4
                'size' => $class->getCollectionSize(),
305
                'max' => $class->getCollectionMax(),
306 4
            ]
307 4
        );
308 4
    }
309
310
    /**
311 4
     * Drop all the mapped document collections in the metadata factory.
312
     */
313 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
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
316 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
317
                continue;
318 1
            }
319 1
            $this->dropDocumentCollection($class->name);
320 1
        }
321
    }
322 1
323
    /**
324 1
     * Drop the document collection for a mapped class.
325
     *
326
     * @param string $documentName
327
     * @throws \InvalidArgumentException
328
     */
329 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
        $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 4
        }
335 4
        $this->dm->getDocumentCollection($documentName)->drop();
336
    }
337
338 4
    /**
339 4
     * Drop all the mapped document databases in the metadata factory.
340
     */
341 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
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
344 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
345
                continue;
346 1
            }
347 1
            $this->dropDocumentDatabase($class->name);
348 1
        }
349
    }
350 1
351
    /**
352 1
     * Drop the document database for a mapped class.
353
     *
354
     * @param string $documentName
355
     * @throws \InvalidArgumentException
356
     */
357 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
        $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 2
        }
363 2
        $this->dm->getDocumentDatabase($documentName)->drop();
364
    }
365
366 2
    /**
367 2
     * 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
    public function isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex)
389
    {
390
        $documentIndexOptions = $documentIndex['options'];
391 30
392
        if ($mongoIndex['key'] !== $documentIndex['keys']) {
393 30
            return false;
394
        }
395 30
396 2
        if (empty($mongoIndex['sparse']) xor empty($documentIndexOptions['sparse'])) {
397
            return false;
398
        }
399 28
400 2
        if (empty($mongoIndex['unique']) xor empty($documentIndexOptions['unique'])) {
401
            return false;
402
        }
403 26
404 2
        if (! empty($mongoIndex['unique']) && empty($mongoIndex['dropDups']) &&
405
            ! empty($documentIndexOptions['unique']) && ! empty($documentIndexOptions['dropDups'])) {
406
            return false;
407 24
        }
408 24
409 1
        foreach (['bits', 'max', 'min'] as $option) {
410
            if (isset($mongoIndex[$option]) xor isset($documentIndexOptions[$option])) {
411
                return false;
412 23
            }
413 23
414 6
            if (isset($mongoIndex[$option]) && isset($documentIndexOptions[$option]) &&
415
                $mongoIndex[$option] !== $documentIndexOptions[$option]) {
416
                return false;
417 21
            }
418 21
        }
419 21
420
        if (empty($mongoIndex['partialFilterExpression']) xor empty($documentIndexOptions['partialFilterExpression'])) {
421
            return false;
422
        }
423 14
424 2
        if (isset($mongoIndex['partialFilterExpression']) && isset($documentIndexOptions['partialFilterExpression']) &&
425
            $mongoIndex['partialFilterExpression'] !== $documentIndexOptions['partialFilterExpression']) {
426
            return false;
427 12
        }
428 12
429 1
        return true;
430
    }
431
432 11
    /**
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
    public function ensureDocumentSharding($documentName, array $indexOptions = [])
460
    {
461
        $class = $this->dm->getClassMetadata($documentName);
462 2
        if (! $class->isSharded()) {
463
            return;
464 2
        }
465 2
466
        $this->enableShardingForDbByDocumentName($documentName);
467
468
        $try = 0;
469 2
        do {
470
            try {
471 2
                $result = $this->runShardCollectionCommand($documentName);
472
                $done = true;
473
474 2
                // 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 2
                    $key = $result['proposedKey'] ?? $this->dm->getClassMetadata($documentName)->getShardKey()['keys'];
479
480
                    $this->dm->getDocumentCollection($documentName)->ensureIndex($key, $indexOptions);
481
                    $done = false;
482
                }
483
            } 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 2
                if ($e->getCode() === 20 || $e->getCode() === 23 || $e->getMessage() === 'already sharded') {
485
                    return;
486 1
                }
487 1
488 1
                throw $e;
489
            }
490
        } 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 2
        // For older MongoDB versions, check the error message
494
        if ((bool) $result['ok'] || (isset($result['code']) && $result['code'] === 20) || $result['errmsg'] === 'already sharded') {
495
            return;
496
        }
497 2
498 2
        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
    public function enableShardingForDbByDocumentName($documentName)
509
    {
510
        $dbName = $this->dm->getDocumentDatabase($documentName)->getDatabaseName();
511 2
        $adminDb = $this->dm->getClient()->selectDatabase('admin');
512
513 2
        try {
514 2
            $adminDb->command(['enableSharding' => $dbName]);
515
        } 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
            if ($e->getCode() !== 23 || $e->getMessage() === 'already enabled') {
517 2
                throw MongoDBException::failedToEnableSharding($dbName, $e->getMessage());
518 1
            }
519 1
        }
520
    }
521
522
    /**
523 2
     * @param string $documentName
524
     *
525
     * @return array
526
     */
527
    private function runShardCollectionCommand($documentName)
528
    {
529
        $class = $this->dm->getClassMetadata($documentName);
530 2
        $dbName = $this->dm->getDocumentDatabase($documentName)->getDatabaseName();
531
        $shardKey = $class->getShardKey();
532 2
        $adminDb = $this->dm->getClient()->selectDatabase('admin');
533 2
534 2
        $result = $adminDb->command(
535 2
            [
536
                'shardCollection' => $dbName . '.' . $class->getCollection(),
537 2
                'key'             => $shardKey['keys'],
538
            ]
539 2
        )->toArray()[0];
540 2
541
        return $result;
542 2
    }
543
}
544