Completed
Pull Request — master (#1848)
by Andreas
21:00
created

SchemaManager::dropDocumentDatabase()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4.074

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 6
cp 0.8333
rs 9.9666
c 0
b 0
f 0
cc 4
nc 2
nop 1
crap 4.074
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\CommandException;
10
use MongoDB\Model\IndexInfo;
11
use function array_filter;
12
use function array_unique;
13
use function count;
14
use function iterator_to_array;
15
use function ksort;
16
17
class SchemaManager
18
{
19
    private const GRIDFS_FILE_COLLECTION_INDEX = ['files_id' => 1, 'n' => 1];
20
21
    private const GRIDFS_CHUNKS_COLLECTION_INDEX = ['filename' => 1, 'uploadDate' => 1];
22
23
    /** @var DocumentManager */
24
    protected $dm;
25
26
    /** @var ClassMetadataFactory */
27
    protected $metadataFactory;
28
29 1665
    public function __construct(DocumentManager $dm, ClassMetadataFactory $cmf)
30
    {
31 1665
        $this->dm = $dm;
32 1665
        $this->metadataFactory = $cmf;
33 1665
    }
34
35
    /**
36
     * Ensure indexes are created for all documents that can be loaded with the
37
     * metadata factory.
38
     */
39 1
    public function ensureIndexes(?int $timeout = null): void
40
    {
41 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
42 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
43 1
                continue;
44
            }
45
46 1
            $this->ensureDocumentIndexes($class->name, $timeout);
47
        }
48 1
    }
49
50
    /**
51
     * Ensure indexes exist for all mapped document classes.
52
     *
53
     * Indexes that exist in MongoDB but not the document metadata will be
54
     * deleted.
55
     */
56
    public function updateIndexes(?int $timeout = null): void
57
    {
58
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
59
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
60
                continue;
61
            }
62
63
            $this->updateDocumentIndexes($class->name, $timeout);
64
        }
65
    }
66
67
    /**
68
     * Ensure indexes exist for the mapped document class.
69
     *
70
     * Indexes that exist in MongoDB but not the document metadata will be
71
     * deleted.
72
     *
73
     * @throws \InvalidArgumentException
74
     */
75 3
    public function updateDocumentIndexes(string $documentName, ?int $timeout = null): void
76
    {
77 3
        $class = $this->dm->getClassMetadata($documentName);
78
79 3
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
80
            throw new \InvalidArgumentException('Cannot update document indexes for mapped super classes, embedded documents or aggregation result documents.');
81
        }
82
83 3
        $documentIndexes = $this->getDocumentIndexes($documentName);
84 3
        $collection = $this->dm->getDocumentCollection($documentName);
85 3
        $mongoIndexes = iterator_to_array($collection->listIndexes());
86
87
        /* Determine which Mongo indexes should be deleted. Exclude the ID index
88
         * and those that are equivalent to any in the class metadata.
89
         */
90 3
        $self = $this;
91
        $mongoIndexes = array_filter($mongoIndexes, function (IndexInfo $mongoIndex) use ($documentIndexes, $self) {
92 1
            if ($mongoIndex['name'] === '_id_') {
93
                return false;
94
            }
95
96 1
            foreach ($documentIndexes as $documentIndex) {
97 1
                if ($self->isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex)) {
98 1
                    return false;
99
                }
100
            }
101
102 1
            return true;
103 3
        });
104
105
        // Delete indexes that do not exist in class metadata
106 3
        foreach ($mongoIndexes as $mongoIndex) {
107 1
            if (! isset($mongoIndex['name'])) {
108
                continue;
109
            }
110
111 1
            $collection->dropIndex($mongoIndex['name']);
112
        }
113
114 3
        $this->ensureDocumentIndexes($documentName, $timeout);
115 3
    }
116
117 20
    public function getDocumentIndexes(string $documentName): array
118
    {
119 20
        $visited = [];
120 20
        return $this->doGetDocumentIndexes($documentName, $visited);
121
    }
122
123 20
    private function doGetDocumentIndexes(string $documentName, array &$visited): array
124
    {
125 20
        if (isset($visited[$documentName])) {
126 1
            return [];
127
        }
128
129 20
        $visited[$documentName] = true;
130
131 20
        $class = $this->dm->getClassMetadata($documentName);
132 20
        $indexes = $this->prepareIndexes($class);
133 20
        $embeddedDocumentIndexes = [];
134
135
        // Add indexes from embedded & referenced documents
136 20
        foreach ($class->fieldMappings as $fieldMapping) {
137 20
            if (isset($fieldMapping['embedded'])) {
138 4
                if (isset($fieldMapping['targetDocument'])) {
139 4
                    $possibleEmbeds = [$fieldMapping['targetDocument']];
140 2
                } elseif (isset($fieldMapping['discriminatorMap'])) {
141 1
                    $possibleEmbeds = array_unique($fieldMapping['discriminatorMap']);
142
                } else {
143 1
                    continue;
144
                }
145
146 4
                foreach ($possibleEmbeds as $embed) {
147 4
                    if (isset($embeddedDocumentIndexes[$embed])) {
148 2
                        $embeddedIndexes = $embeddedDocumentIndexes[$embed];
149
                    } else {
150 4
                        $embeddedIndexes = $this->doGetDocumentIndexes($embed, $visited);
151 4
                        $embeddedDocumentIndexes[$embed] = $embeddedIndexes;
152
                    }
153
154 4
                    foreach ($embeddedIndexes as $embeddedIndex) {
155 2
                        foreach ($embeddedIndex['keys'] as $key => $value) {
156 2
                            $embeddedIndex['keys'][$fieldMapping['name'] . '.' . $key] = $value;
157 2
                            unset($embeddedIndex['keys'][$key]);
158
                        }
159
160 4
                        $indexes[] = $embeddedIndex;
161
                    }
162
                }
163 20
            } elseif (isset($fieldMapping['reference']) && isset($fieldMapping['targetDocument'])) {
164 9
                foreach ($indexes as $idx => $index) {
165 8
                    $newKeys = [];
166 8
                    foreach ($index['keys'] as $key => $v) {
167 8
                        if ($key === $fieldMapping['name']) {
168 2
                            $key = ClassMetadata::getReferenceFieldName($fieldMapping['storeAs'], $key);
169
                        }
170
171 8
                        $newKeys[$key] = $v;
172
                    }
173
174 20
                    $indexes[$idx]['keys'] = $newKeys;
175
                }
176
            }
177
        }
178
179 20
        return $indexes;
180
    }
181
182 20
    private function prepareIndexes(ClassMetadata $class): array
183
    {
184 20
        $persister = $this->dm->getUnitOfWork()->getDocumentPersister($class->name);
185 20
        $indexes = $class->getIndexes();
186 20
        $newIndexes = [];
187
188 20
        foreach ($indexes as $index) {
189
            $newIndex = [
190 19
                'keys' => [],
191 19
                'options' => $index['options'],
192
            ];
193
194 19
            foreach ($index['keys'] as $key => $value) {
195 19
                $key = $persister->prepareFieldName($key);
196 19
                if ($class->hasField($key)) {
197 17
                    $mapping = $class->getFieldMapping($key);
198 17
                    $newIndex['keys'][$mapping['name']] = $value;
199
                } else {
200 19
                    $newIndex['keys'][$key] = $value;
201
                }
202
            }
203
204 19
            $newIndexes[] = $newIndex;
205
        }
206
207 20
        return $newIndexes;
208
    }
209
210
    /**
211
     * Ensure the given document's indexes are created.
212
     *
213
     * @throws \InvalidArgumentException
214
     */
215 16
    public function ensureDocumentIndexes(string $documentName, ?int $timeoutMs = null): void
216
    {
217 16
        $class = $this->dm->getClassMetadata($documentName);
218 16
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
219
            throw new \InvalidArgumentException('Cannot create document indexes for mapped super classes, embedded documents or query result documents.');
220
        }
221
222 16
        if ($class->isFile) {
223 2
            $this->ensureGridFSIndexes($class);
224
        }
225
226 16
        $indexes = $this->getDocumentIndexes($documentName);
227 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...
228 4
            return;
229
        }
230
231 15
        $collection = $this->dm->getDocumentCollection($class->name);
232 15
        foreach ($indexes as $index) {
233 15
            $keys = $index['keys'];
234 15
            $options = $index['options'];
235
236 15
            if (! isset($options['timeout']) && isset($timeoutMs)) {
237 1
                $options['timeout'] = $timeoutMs;
238
            }
239
240 15
            $collection->createIndex($keys, $options);
241
        }
242 15
    }
243
244
    /**
245
     * Delete indexes for all documents that can be loaded with the
246
     * metadata factory.
247
     */
248 1
    public function deleteIndexes(): void
249
    {
250 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
251 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
252 1
                continue;
253
            }
254
255 1
            $this->deleteDocumentIndexes($class->name);
256
        }
257 1
    }
258
259
    /**
260
     * Delete the given document's indexes.
261
     *
262
     * @throws \InvalidArgumentException
263
     */
264 2
    public function deleteDocumentIndexes(string $documentName): void
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
271 2
        $this->dm->getDocumentCollection($documentName)->dropIndexes();
272 2
    }
273
274
    /**
275
     * Create all the mapped document collections in the metadata factory.
276
     */
277 1
    public function createCollections(): void
278
    {
279 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
280 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
281 1
                continue;
282
            }
283 1
            $this->createDocumentCollection($class->name);
284
        }
285 1
    }
286
287
    /**
288
     * Create the document collection for a mapped class.
289
     *
290
     * @throws \InvalidArgumentException
291
     */
292 5
    public function createDocumentCollection(string $documentName): void
293
    {
294 5
        $class = $this->dm->getClassMetadata($documentName);
295
296 5
        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 5
        if ($class->isFile) {
301 2
            $this->dm->getDocumentDatabase($documentName)->createCollection($class->getBucketName() . '.files');
302 2
            $this->dm->getDocumentDatabase($documentName)->createCollection($class->getBucketName() . '.chunks');
303
304 2
            return;
305
        }
306
307 4
        $this->dm->getDocumentDatabase($documentName)->createCollection(
308 4
            $class->getCollection(),
309
            [
310 4
                'capped' => $class->getCollectionCapped(),
311 4
                'size' => $class->getCollectionSize(),
312 4
                'max' => $class->getCollectionMax(),
313
            ]
314
        );
315 4
    }
316
317
    /**
318
     * Drop all the mapped document collections in the metadata factory.
319
     */
320 1
    public function dropCollections(): void
321
    {
322 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
323 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
324 1
                continue;
325
            }
326
327 1
            $this->dropDocumentCollection($class->name);
328
        }
329 1
    }
330
331
    /**
332
     * Drop the document collection for a mapped class.
333
     *
334
     * @throws \InvalidArgumentException
335
     */
336 5
    public function dropDocumentCollection(string $documentName): void
337
    {
338 5
        $class = $this->dm->getClassMetadata($documentName);
339 5
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
340
            throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes, embedded documents or query result documents.');
341
        }
342
343 5
        $this->dm->getDocumentCollection($documentName)->drop();
344
345 5
        if (! $class->isFile) {
346 4
            return;
347
        }
348
349 2
        $this->dm->getDocumentBucket($documentName)->getChunksCollection()->drop();
350 2
    }
351
352
    /**
353
     * Drop all the mapped document databases in the metadata factory.
354
     */
355 1
    public function dropDatabases(): void
356
    {
357 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
358 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
359 1
                continue;
360
            }
361
362 1
            $this->dropDocumentDatabase($class->name);
363
        }
364 1
    }
365
366
    /**
367
     * Drop the document database for a mapped class.
368
     *
369
     * @throws \InvalidArgumentException
370
     */
371 2
    public function dropDocumentDatabase(string $documentName): void
372
    {
373 2
        $class = $this->dm->getClassMetadata($documentName);
374 2
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
375
            throw new \InvalidArgumentException('Cannot drop document database for mapped super classes, embedded documents or query result documents.');
376
        }
377
378 2
        $this->dm->getDocumentDatabase($documentName)->drop();
379 2
    }
380
381
    /**
382
     * Determine if an index returned by MongoCollection::getIndexInfo() can be
383
     * considered equivalent to an index in class metadata.
384
     *
385
     * Indexes are considered different if:
386
     *
387
     *   (a) Key/direction pairs differ or are not in the same order
388
     *   (b) Sparse or unique options differ
389
     *   (c) Mongo index is unique without dropDups and mapped index is unique
390
     *       with dropDups
391
     *   (d) Geospatial options differ (bits, max, min)
392
     *   (e) The partialFilterExpression differs
393
     *
394
     * Regarding (c), the inverse case is not a reason to delete and
395
     * recreate the index, since dropDups only affects creation of
396
     * the unique index. Additionally, the background option is only
397
     * relevant to index creation and is not considered.
398
     *
399
     * @param array|IndexInfo $mongoIndex Mongo index data.
400
     */
401 47
    public function isMongoIndexEquivalentToDocumentIndex($mongoIndex, array $documentIndex): bool
402
    {
403 47
        $documentIndexOptions = $documentIndex['options'];
404
405 47
        if (! $this->isEquivalentIndexKeys($mongoIndex, $documentIndex)) {
406 4
            return false;
407
        }
408
409 43
        if (empty($mongoIndex['sparse']) xor empty($documentIndexOptions['sparse'])) {
410 2
            return false;
411
        }
412
413 41
        if (empty($mongoIndex['unique']) xor empty($documentIndexOptions['unique'])) {
414 2
            return false;
415
        }
416
417 39
        if (! empty($mongoIndex['unique']) && empty($mongoIndex['dropDups']) &&
418 39
            ! empty($documentIndexOptions['unique']) && ! empty($documentIndexOptions['dropDups'])) {
419 1
            return false;
420
        }
421
422 38
        foreach (['bits', 'max', 'min'] as $option) {
423 38
            if (isset($mongoIndex[$option]) xor isset($documentIndexOptions[$option])) {
424 6
                return false;
425
            }
426
427 36
            if (isset($mongoIndex[$option], $documentIndexOptions[$option]) &&
428 36
                $mongoIndex[$option] !== $documentIndexOptions[$option]) {
429 36
                return false;
430
            }
431
        }
432
433 29
        if (empty($mongoIndex['partialFilterExpression']) xor empty($documentIndexOptions['partialFilterExpression'])) {
434 2
            return false;
435
        }
436
437 27
        if (isset($mongoIndex['partialFilterExpression'], $documentIndexOptions['partialFilterExpression']) &&
438 27
            $mongoIndex['partialFilterExpression'] !== $documentIndexOptions['partialFilterExpression']) {
439 1
            return false;
440
        }
441
442 26
        if (isset($mongoIndex['weights']) && ! $this->isEquivalentTextIndexWeights($mongoIndex, $documentIndex)) {
443 2
            return false;
444
        }
445
446 24
        foreach (['default_language', 'language_override', 'textIndexVersion'] as $option) {
447
            /* Text indexes will always report defaults for these options, so
448
             * only compare if we have explicit values in the document index. */
449 24
            if (isset($mongoIndex[$option], $documentIndexOptions[$option]) &&
450 24
                $mongoIndex[$option] !== $documentIndexOptions[$option]) {
451 24
                return false;
452
            }
453
        }
454
455 21
        return true;
456
    }
457
458
    /**
459
     * Determine if the keys for a MongoDB index can be considered equivalent to
460
     * those for an index in class metadata.
461
     *
462
     * @param array|IndexInfo $mongoIndex Mongo index data.
463
     */
464 47
    private function isEquivalentIndexKeys($mongoIndex, array $documentIndex): bool
465
    {
466 47
        $mongoIndexKeys    = $mongoIndex['key'];
467 47
        $documentIndexKeys = $documentIndex['keys'];
468
469
        /* If we are dealing with text indexes, we need to unset internal fields
470
         * from the MongoDB index and filter out text fields from the document
471
         * index. This will leave only non-text fields, which we can compare as
472
         * normal. Any text fields in the document index will be compared later
473
         * with isEquivalentTextIndexWeights(). */
474 47
        if (isset($mongoIndexKeys['_fts']) && $mongoIndexKeys['_fts'] === 'text') {
475 15
            unset($mongoIndexKeys['_fts'], $mongoIndexKeys['_ftsx']);
476
477
            $documentIndexKeys = array_filter($documentIndexKeys, function ($type) {
478 15
                return $type !== 'text';
479 15
            });
480
        }
481
482
        /* Avoid a strict equality check here. The numeric type returned by
483
         * MongoDB may differ from the document index without implying that the
484
         * indexes themselves are inequivalent. */
485
        // phpcs:disable SlevomatCodingStandard.ControlStructures.DisallowEqualOperators.DisallowedEqualOperator
486 47
        return $mongoIndexKeys == $documentIndexKeys;
487
    }
488
489
    /**
490
     * Determine if the text index weights for a MongoDB index can be considered
491
     * equivalent to those for an index in class metadata.
492
     *
493
     * @param array|IndexInfo $mongoIndex Mongo index data.
494
     */
495 14
    private function isEquivalentTextIndexWeights($mongoIndex, array $documentIndex): bool
496
    {
497 14
        $mongoIndexWeights    = $mongoIndex['weights'];
498 14
        $documentIndexWeights = $documentIndex['options']['weights'] ?? [];
499
500
        // If not specified, assign a default weight for text fields
501 14
        foreach ($documentIndex['keys'] as $key => $type) {
502 14
            if ($type !== 'text' || isset($documentIndexWeights[$key])) {
503 5
                continue;
504
            }
505
506 9
            $documentIndexWeights[$key] = 1;
507
        }
508
509
        /* MongoDB returns the weights sorted by field name, but we'll sort both
510
         * arrays in case that is internal behavior not to be relied upon. */
511 14
        ksort($mongoIndexWeights);
512 14
        ksort($documentIndexWeights);
513
514
        /* Avoid a strict equality check here. The numeric type returned by
515
         * MongoDB may differ from the document index without implying that the
516
         * indexes themselves are inequivalent. */
517
        // phpcs:disable SlevomatCodingStandard.ControlStructures.DisallowEqualOperators.DisallowedEqualOperator
518 14
        return $mongoIndexWeights == $documentIndexWeights;
519
    }
520
521
    /**
522
     * Ensure collections are sharded for all documents that can be loaded with the
523
     * metadata factory.
524
     *
525
     * @throws MongoDBException
526
     */
527
    public function ensureSharding(): void
528
    {
529
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
530
            if ($class->isMappedSuperclass || ! $class->isSharded()) {
531
                continue;
532
            }
533
534
            $this->ensureDocumentSharding($class->name);
535
        }
536
    }
537
538
    /**
539
     * Ensure sharding for collection by document name.
540
     *
541
     * @throws MongoDBException
542
     */
543
    public function ensureDocumentSharding(string $documentName): void
544
    {
545
        $class = $this->dm->getClassMetadata($documentName);
546
        if (! $class->isSharded()) {
547 2
            return;
548
        }
549 2
550 2
        if ($this->collectionIsSharded($documentName)) {
551
            return;
552
        }
553
554 2
        $this->enableShardingForDbByDocumentName($documentName);
555
556 2
        try {
557
            $this->runShardCollectionCommand($documentName);
558
        } catch (CommandException $e) {
0 ignored issues
show
Bug introduced by
The class MongoDB\Driver\Exception\CommandException 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...
559 2
            throw MongoDBException::failedToEnsureDocumentSharding($documentName, $e->getMessage());
560 2
        }
561
    }
562
563 2
    /**
564
     * Enable sharding for database which contains documents with given name.
565
     *
566
     * @throws MongoDBException
567
     */
568
    public function enableShardingForDbByDocumentName(string $documentName): void
569 2
    {
570
        $dbName = $this->dm->getDocumentDatabase($documentName)->getDatabaseName();
571 1
        $adminDb = $this->dm->getClient()->selectDatabase('admin');
572 1
573 1
        try {
574
            $adminDb->command(['enableSharding' => $dbName]);
575
        } catch (CommandException $e) {
0 ignored issues
show
Bug introduced by
The class MongoDB\Driver\Exception\CommandException 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...
576
            if ($e->getCode() !== 23 || $e->getMessage() === 'already enabled') {
577
                throw MongoDBException::failedToEnableSharding($dbName, $e->getMessage());
578 2
            }
579
        }
580
    }
581
582 2
    private function runShardCollectionCommand(string $documentName): array
583 2
    {
584
        $class = $this->dm->getClassMetadata($documentName);
585
        $dbName = $this->dm->getDocumentDatabase($documentName)->getDatabaseName();
586
        $shardKey = $class->getShardKey();
587
        $adminDb = $this->dm->getClient()->selectDatabase('admin');
588
589
        $result = $adminDb->command(
590
            [
591
                'shardCollection' => $dbName . '.' . $class->getCollection(),
592
                'key'             => $shardKey['keys'],
593
            ]
594 2
        )->toArray()[0];
595
596 2
        return $result;
597 2
    }
598
599
    private function ensureGridFSIndexes(ClassMetadata $class): void
600 2
    {
601 1
        $this->ensureChunksIndex($class);
602 1
        $this->ensureFilesIndex($class);
603
    }
604
605
    private function ensureChunksIndex(ClassMetadata $class): void
606 2
    {
607
        $chunksCollection = $this->dm->getDocumentBucket($class->getName())->getChunksCollection();
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
608 2
        foreach ($chunksCollection->listIndexes() as $index) {
609
            if ($index->isUnique() && $index->getKey() === self::GRIDFS_FILE_COLLECTION_INDEX) {
610 2
                return;
611 2
            }
612 2
        }
613 2
614
        $chunksCollection->createIndex(self::GRIDFS_FILE_COLLECTION_INDEX, ['unique' => true]);
615 2
    }
616
617 2
    private function ensureFilesIndex(ClassMetadata $class): void
618 2
    {
619
        $filesCollection = $this->dm->getDocumentCollection($class->getName());
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
620 2
        foreach ($filesCollection->listIndexes() as $index) {
621
            if ($index->getKey() === self::GRIDFS_CHUNKS_COLLECTION_INDEX) {
622 2
                return;
623
            }
624
        }
625 2
626
        $filesCollection->createIndex(self::GRIDFS_CHUNKS_COLLECTION_INDEX);
627 2
    }
628 2
629 2
    private function collectionIsSharded(string $documentName): bool
630
    {
631 2
        $class = $this->dm->getClassMetadata($documentName);
632
633 2
        $database = $this->dm->getDocumentDatabase($documentName);
634 2
        $collections = iterator_to_array($database->listCollections(['filter' => ['name' => $class->getCollection()]]));
635
        if (! count($collections)) {
636
            return false;
637
        }
638
639
        $stats = $database->command(['collstats' => $class->getCollection()])->toArray()[0];
640 2
        return $stats['sharded'] ?? false;
641 2
    }
642
}
643