Completed
Pull Request — master (#1693)
by Andreas
15:14
created

SchemaManager::deleteIndexes()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 9
Ratio 100 %

Code Coverage

Tests 6
CRAP Score 5

Importance

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