These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /* |
||
3 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||
4 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||
5 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||
6 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||
7 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||
8 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||
9 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||
10 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||
11 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
12 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||
13 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
14 | * |
||
15 | * This software consists of voluntary contributions made by many individuals |
||
16 | * and is licensed under the MIT license. For more information, see |
||
17 | * <http://www.doctrine-project.org>. |
||
18 | */ |
||
19 | |||
20 | namespace Doctrine\ODM\MongoDB; |
||
21 | |||
22 | use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; |
||
23 | use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory; |
||
24 | use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo; |
||
25 | |||
26 | class SchemaManager |
||
27 | { |
||
28 | /** |
||
29 | * @var DocumentManager |
||
30 | */ |
||
31 | protected $dm; |
||
32 | |||
33 | /** |
||
34 | * |
||
35 | * @var ClassMetadataFactory |
||
36 | */ |
||
37 | protected $metadataFactory; |
||
38 | |||
39 | /** |
||
40 | * @param DocumentManager $dm |
||
41 | * @param ClassMetadataFactory $cmf |
||
42 | */ |
||
43 | 1144 | public function __construct(DocumentManager $dm, ClassMetadataFactory $cmf) |
|
44 | { |
||
45 | 1144 | $this->dm = $dm; |
|
46 | 1144 | $this->metadataFactory = $cmf; |
|
47 | 1144 | } |
|
48 | |||
49 | /** |
||
50 | * Ensure indexes are created for all documents that can be loaded with the |
||
51 | * metadata factory. |
||
52 | * |
||
53 | * @param integer $timeout Timeout (ms) for acknowledged index creation |
||
54 | */ |
||
55 | 1 | View Code Duplication | public function ensureIndexes($timeout = null) |
56 | { |
||
57 | 1 | foreach ($this->metadataFactory->getAllMetadata() as $class) { |
|
58 | 1 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) { |
|
59 | 1 | continue; |
|
60 | } |
||
61 | 1 | $this->ensureDocumentIndexes($class->name, $timeout); |
|
62 | } |
||
63 | 1 | } |
|
64 | |||
65 | /** |
||
66 | * Ensure indexes exist for all mapped document classes. |
||
67 | * |
||
68 | * Indexes that exist in MongoDB but not the document metadata will be |
||
69 | * deleted. |
||
70 | * |
||
71 | * @param integer $timeout Timeout (ms) for acknowledged index creation |
||
72 | */ |
||
73 | View Code Duplication | public function updateIndexes($timeout = null) |
|
74 | { |
||
75 | foreach ($this->metadataFactory->getAllMetadata() as $class) { |
||
76 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) { |
||
77 | continue; |
||
78 | } |
||
79 | $this->updateDocumentIndexes($class->name, $timeout); |
||
80 | } |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * Ensure indexes exist for the mapped document class. |
||
85 | * |
||
86 | * Indexes that exist in MongoDB but not the document metadata will be |
||
87 | * deleted. |
||
88 | * |
||
89 | * @param string $documentName |
||
90 | * @param integer $timeout Timeout (ms) for acknowledged index creation |
||
91 | * @throws \InvalidArgumentException |
||
92 | */ |
||
93 | 3 | public function updateDocumentIndexes($documentName, $timeout = null) |
|
94 | { |
||
95 | 3 | $class = $this->dm->getClassMetadata($documentName); |
|
96 | |||
97 | 3 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) { |
|
98 | throw new \InvalidArgumentException('Cannot update document indexes for mapped super classes, embedded documents or aggregation result documents.'); |
||
99 | } |
||
100 | |||
101 | 3 | $documentIndexes = $this->getDocumentIndexes($documentName); |
|
102 | 3 | $collection = $this->dm->getDocumentCollection($documentName); |
|
103 | 3 | $mongoIndexes = $collection->getIndexInfo(); |
|
104 | |||
105 | /* Determine which Mongo indexes should be deleted. Exclude the ID index |
||
106 | * and those that are equivalent to any in the class metadata. |
||
107 | */ |
||
108 | 3 | $self = $this; |
|
109 | 3 | $mongoIndexes = array_filter($mongoIndexes, function ($mongoIndex) use ($documentIndexes, $self) { |
|
110 | 1 | if ('_id_' === $mongoIndex['name']) { |
|
111 | return false; |
||
112 | } |
||
113 | |||
114 | 1 | foreach ($documentIndexes as $documentIndex) { |
|
115 | 1 | if ($self->isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex)) { |
|
116 | return false; |
||
117 | } |
||
118 | } |
||
119 | |||
120 | 1 | return true; |
|
121 | 3 | }); |
|
122 | |||
123 | // Delete indexes that do not exist in class metadata |
||
124 | 3 | foreach ($mongoIndexes as $mongoIndex) { |
|
125 | 1 | if (isset($mongoIndex['name'])) { |
|
126 | /* Note: MongoCollection::deleteIndex() cannot delete |
||
127 | * custom-named indexes, so use the deleteIndexes command. |
||
128 | */ |
||
129 | 1 | $collection->getDatabase()->command(array( |
|
130 | 1 | 'deleteIndexes' => $collection->getName(), |
|
131 | 1 | 'index' => $mongoIndex['name'], |
|
132 | )); |
||
133 | } |
||
134 | } |
||
135 | |||
136 | 3 | $this->ensureDocumentIndexes($documentName, $timeout); |
|
137 | 3 | } |
|
138 | |||
139 | /** |
||
140 | * @param string $documentName |
||
141 | * @return array |
||
142 | */ |
||
143 | 48 | public function getDocumentIndexes($documentName) |
|
144 | { |
||
145 | 48 | $visited = array(); |
|
146 | 48 | return $this->doGetDocumentIndexes($documentName, $visited); |
|
147 | } |
||
148 | |||
149 | /** |
||
150 | * @param string $documentName |
||
151 | * @param array $visited |
||
152 | * @return array |
||
153 | */ |
||
154 | 48 | private function doGetDocumentIndexes($documentName, array &$visited) |
|
155 | { |
||
156 | 48 | if (isset($visited[$documentName])) { |
|
157 | 1 | return array(); |
|
158 | } |
||
159 | |||
160 | 48 | $visited[$documentName] = true; |
|
161 | |||
162 | 48 | $class = $this->dm->getClassMetadata($documentName); |
|
163 | 48 | $indexes = $this->prepareIndexes($class); |
|
164 | 48 | $embeddedDocumentIndexes = array(); |
|
165 | |||
166 | // Add indexes from embedded & referenced documents |
||
167 | 48 | foreach ($class->fieldMappings as $fieldMapping) { |
|
168 | 48 | if (isset($fieldMapping['embedded'])) { |
|
169 | 31 | if (isset($fieldMapping['targetDocument'])) { |
|
170 | 31 | $possibleEmbeds = array($fieldMapping['targetDocument']); |
|
171 | 2 | } elseif (isset($fieldMapping['discriminatorMap'])) { |
|
172 | 1 | $possibleEmbeds = array_unique($fieldMapping['discriminatorMap']); |
|
173 | } else { |
||
174 | 1 | continue; |
|
175 | } |
||
176 | 31 | foreach ($possibleEmbeds as $embed) { |
|
177 | 31 | if (isset($embeddedDocumentIndexes[$embed])) { |
|
178 | 25 | $embeddedIndexes = $embeddedDocumentIndexes[$embed]; |
|
179 | } else { |
||
180 | 31 | $embeddedIndexes = $this->doGetDocumentIndexes($embed, $visited); |
|
181 | 31 | $embeddedDocumentIndexes[$embed] = $embeddedIndexes; |
|
182 | } |
||
183 | 31 | foreach ($embeddedIndexes as $embeddedIndex) { |
|
184 | 25 | foreach ($embeddedIndex['keys'] as $key => $value) { |
|
185 | 25 | $embeddedIndex['keys'][$fieldMapping['name'] . '.' . $key] = $value; |
|
186 | 25 | unset($embeddedIndex['keys'][$key]); |
|
187 | } |
||
188 | 31 | $indexes[] = $embeddedIndex; |
|
189 | } |
||
190 | } |
||
191 | 48 | } elseif (isset($fieldMapping['reference']) && isset($fieldMapping['targetDocument'])) { |
|
192 | 31 | foreach ($indexes as $idx => $index) { |
|
193 | 31 | $newKeys = array(); |
|
194 | 31 | foreach ($index['keys'] as $key => $v) { |
|
195 | 31 | if ($key == $fieldMapping['name']) { |
|
196 | 2 | $key = $fieldMapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID |
|
197 | 2 | ? $key |
|
198 | 2 | : $key . '.$id'; |
|
199 | } |
||
200 | 31 | $newKeys[$key] = $v; |
|
201 | } |
||
202 | 31 | $indexes[$idx]['keys'] = $newKeys; |
|
203 | } |
||
204 | } |
||
205 | } |
||
206 | 48 | return $indexes; |
|
207 | } |
||
208 | |||
209 | /** |
||
210 | * @param ClassMetadata $class |
||
211 | * @return array |
||
212 | */ |
||
213 | 48 | private function prepareIndexes(ClassMetadata $class) |
|
214 | { |
||
215 | 48 | $persister = $this->dm->getUnitOfWork()->getDocumentPersister($class->name); |
|
216 | 48 | $indexes = $class->getIndexes(); |
|
217 | 48 | $newIndexes = array(); |
|
218 | |||
219 | 48 | foreach ($indexes as $index) { |
|
220 | $newIndex = array( |
||
221 | 48 | 'keys' => array(), |
|
222 | 48 | 'options' => $index['options'] |
|
223 | ); |
||
224 | 48 | foreach ($index['keys'] as $key => $value) { |
|
225 | 48 | $key = $persister->prepareFieldName($key); |
|
226 | 48 | if ($class->hasField($key)) { |
|
227 | 46 | $mapping = $class->getFieldMapping($key); |
|
228 | 46 | $newIndex['keys'][$mapping['name']] = $value; |
|
229 | } else { |
||
230 | 5 | $newIndex['keys'][$key] = $value; |
|
231 | } |
||
232 | } |
||
233 | |||
234 | 48 | $newIndexes[] = $newIndex; |
|
235 | } |
||
236 | |||
237 | 48 | return $newIndexes; |
|
238 | } |
||
239 | |||
240 | /** |
||
241 | * Ensure the given document's indexes are created. |
||
242 | * |
||
243 | * @param string $documentName |
||
244 | * @param integer $timeout Timeout (ms) for acknowledged index creation |
||
245 | * @throws \InvalidArgumentException |
||
246 | */ |
||
247 | 44 | public function ensureDocumentIndexes($documentName, $timeout = null) |
|
248 | { |
||
249 | 44 | $class = $this->dm->getClassMetadata($documentName); |
|
250 | 44 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) { |
|
251 | throw new \InvalidArgumentException('Cannot create document indexes for mapped super classes, embedded documents or query result documents.'); |
||
252 | } |
||
253 | 44 | if ($indexes = $this->getDocumentIndexes($documentName)) { |
|
254 | 44 | $collection = $this->dm->getDocumentCollection($class->name); |
|
255 | 44 | foreach ($indexes as $index) { |
|
256 | 44 | $keys = $index['keys']; |
|
257 | 44 | $options = $index['options']; |
|
258 | |||
259 | 44 | if ( ! isset($options['safe']) && ! isset($options['w'])) { |
|
260 | 43 | $options['w'] = 1; |
|
261 | } |
||
262 | |||
263 | 44 | if (isset($options['safe']) && ! isset($options['w'])) { |
|
264 | 1 | $options['w'] = is_bool($options['safe']) ? (integer) $options['safe'] : $options['safe']; |
|
265 | 1 | unset($options['safe']); |
|
266 | } |
||
267 | |||
268 | 44 | if ( ! isset($options['timeout']) && isset($timeout)) { |
|
269 | 1 | $options['timeout'] = $timeout; |
|
270 | } |
||
271 | |||
272 | 44 | $collection->ensureIndex($keys, $options); |
|
273 | } |
||
274 | } |
||
275 | 44 | } |
|
276 | |||
277 | /** |
||
278 | * Delete indexes for all documents that can be loaded with the |
||
279 | * metadata factory. |
||
280 | */ |
||
281 | 1 | View Code Duplication | public function deleteIndexes() |
282 | { |
||
283 | 1 | foreach ($this->metadataFactory->getAllMetadata() as $class) { |
|
284 | 1 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) { |
|
285 | 1 | continue; |
|
286 | } |
||
287 | 1 | $this->deleteDocumentIndexes($class->name); |
|
288 | } |
||
289 | 1 | } |
|
290 | |||
291 | /** |
||
292 | * Delete the given document's indexes. |
||
293 | * |
||
294 | * @param string $documentName |
||
295 | * @throws \InvalidArgumentException |
||
296 | */ |
||
297 | 2 | View Code Duplication | public function deleteDocumentIndexes($documentName) |
298 | { |
||
299 | 2 | $class = $this->dm->getClassMetadata($documentName); |
|
300 | 2 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) { |
|
301 | throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes, embedded documents or query result documents.'); |
||
302 | } |
||
303 | 2 | $this->dm->getDocumentCollection($documentName)->deleteIndexes(); |
|
304 | 2 | } |
|
305 | |||
306 | /** |
||
307 | * Create all the mapped document collections in the metadata factory. |
||
308 | */ |
||
309 | 1 | View Code Duplication | public function createCollections() |
310 | { |
||
311 | 1 | foreach ($this->metadataFactory->getAllMetadata() as $class) { |
|
312 | 1 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) { |
|
313 | 1 | continue; |
|
314 | } |
||
315 | 1 | $this->createDocumentCollection($class->name); |
|
316 | } |
||
317 | 1 | } |
|
318 | |||
319 | /** |
||
320 | * Create the document collection for a mapped class. |
||
321 | * |
||
322 | * @param string $documentName |
||
323 | * @throws \InvalidArgumentException |
||
324 | */ |
||
325 | 4 | public function createDocumentCollection($documentName) |
|
326 | { |
||
327 | 4 | $class = $this->dm->getClassMetadata($documentName); |
|
328 | |||
329 | 4 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) { |
|
330 | throw new \InvalidArgumentException('Cannot create document collection for mapped super classes, embedded documents or query result documents.'); |
||
331 | } |
||
332 | |||
333 | 4 | if ($class->isFile()) { |
|
334 | 2 | $this->dm->getDocumentDatabase($documentName)->createCollection($class->getCollection() . '.files'); |
|
335 | 2 | $this->dm->getDocumentDatabase($documentName)->createCollection($class->getCollection() . '.chunks'); |
|
336 | |||
337 | 2 | return; |
|
338 | } |
||
339 | |||
340 | 3 | $this->dm->getDocumentDatabase($documentName)->createCollection( |
|
341 | 3 | $class->getCollection(), |
|
342 | 3 | $class->getCollectionCapped(), |
|
343 | 3 | $class->getCollectionSize(), |
|
344 | 3 | $class->getCollectionMax() |
|
345 | ); |
||
346 | 3 | } |
|
347 | |||
348 | /** |
||
349 | * Drop all the mapped document collections in the metadata factory. |
||
350 | */ |
||
351 | 2 | View Code Duplication | public function dropCollections() |
352 | { |
||
353 | 2 | foreach ($this->metadataFactory->getAllMetadata() as $class) { |
|
354 | 2 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) { |
|
355 | 2 | continue; |
|
356 | } |
||
357 | 2 | $this->dropDocumentCollection($class->name); |
|
358 | } |
||
359 | 2 | } |
|
360 | |||
361 | /** |
||
362 | * Drop the document collection for a mapped class. |
||
363 | * |
||
364 | * @param string $documentName |
||
365 | * @throws \InvalidArgumentException |
||
366 | */ |
||
367 | 4 | View Code Duplication | public function dropDocumentCollection($documentName) |
368 | { |
||
369 | 4 | $class = $this->dm->getClassMetadata($documentName); |
|
370 | 4 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) { |
|
371 | throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes, embedded documents or query result documents.'); |
||
372 | } |
||
373 | 4 | $this->dm->getDocumentCollection($documentName)->drop(); |
|
374 | 4 | } |
|
375 | |||
376 | /** |
||
377 | * Drop all the mapped document databases in the metadata factory. |
||
378 | */ |
||
379 | 1 | View Code Duplication | public function dropDatabases() |
380 | { |
||
381 | 1 | foreach ($this->metadataFactory->getAllMetadata() as $class) { |
|
382 | 1 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) { |
|
383 | 1 | continue; |
|
384 | } |
||
385 | 1 | $this->dropDocumentDatabase($class->name); |
|
386 | } |
||
387 | 1 | } |
|
388 | |||
389 | /** |
||
390 | * Drop the document database for a mapped class. |
||
391 | * |
||
392 | * @param string $documentName |
||
393 | * @throws \InvalidArgumentException |
||
394 | */ |
||
395 | 2 | View Code Duplication | public function dropDocumentDatabase($documentName) |
396 | { |
||
397 | 2 | $class = $this->dm->getClassMetadata($documentName); |
|
398 | 2 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) { |
|
399 | throw new \InvalidArgumentException('Cannot drop document database for mapped super classes, embedded documents or query result documents.'); |
||
400 | } |
||
401 | 2 | $this->dm->getDocumentDatabase($documentName)->drop(); |
|
402 | 2 | } |
|
403 | |||
404 | /** |
||
405 | * Create all the mapped document databases in the metadata factory. |
||
406 | * |
||
407 | * @deprecated Databases are created automatically by MongoDB (>= 3.0). Deprecated since ODM 1.2, to be removed in ODM 2.0. |
||
408 | */ |
||
409 | public function createDatabases() |
||
410 | { |
||
411 | @trigger_error( |
||
0 ignored issues
–
show
|
|||
412 | sprintf('%s was deprecated in version 1.2 - databases are created automatically by MongoDB (>= 3.0).', __METHOD__), |
||
413 | E_USER_DEPRECATED |
||
414 | ); |
||
415 | foreach ($this->metadataFactory->getAllMetadata() as $class) { |
||
416 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) { |
||
417 | continue; |
||
418 | } |
||
419 | $this->createDocumentDatabase($class->name); |
||
420 | } |
||
421 | } |
||
422 | |||
423 | /** |
||
424 | * Create the document database for a mapped class. |
||
425 | * |
||
426 | * @param string $documentName |
||
427 | * @throws \InvalidArgumentException |
||
428 | * |
||
429 | * @deprecated A database is created automatically by MongoDB (>= 3.0). Deprecated since ODM 1.2, to be removed in ODM 2.0. |
||
430 | */ |
||
431 | public function createDocumentDatabase($documentName) |
||
432 | { |
||
433 | @trigger_error( |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
|
|||
434 | sprintf('%s was deprecated in version 1.2 - databases are created automatically by MongoDB (>= 3.0).', __METHOD__), |
||
435 | E_USER_DEPRECATED |
||
436 | ); |
||
437 | $class = $this->dm->getClassMetadata($documentName); |
||
438 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) { |
||
439 | throw new \InvalidArgumentException('Cannot create databases for mapped super classes, embedded documents or query result documents.'); |
||
440 | } |
||
441 | |||
442 | $this->dm->getDocumentDatabase($documentName)->execute('function() { return true; }'); |
||
443 | } |
||
444 | |||
445 | /** |
||
446 | * Determine if an index returned by MongoCollection::getIndexInfo() can be |
||
447 | * considered equivalent to an index in class metadata. |
||
448 | * |
||
449 | * Indexes are considered different if: |
||
450 | * |
||
451 | * (a) Key/direction pairs differ or are not in the same order |
||
452 | * (b) Sparse or unique options differ |
||
453 | * (c) Mongo index is unique without dropDups and mapped index is unique |
||
454 | * with dropDups |
||
455 | * (d) Geospatial options differ (bits, max, min) |
||
456 | * (e) The partialFilterExpression differs |
||
457 | * |
||
458 | * Regarding (c), the inverse case is not a reason to delete and |
||
459 | * recreate the index, since dropDups only affects creation of |
||
460 | * the unique index. Additionally, the background option is only |
||
461 | * relevant to index creation and is not considered. |
||
462 | * |
||
463 | * @param array $mongoIndex Mongo index data. |
||
464 | * @param array $documentIndex Document index data. |
||
465 | * @return bool True if the indexes are equivalent, otherwise false. |
||
466 | */ |
||
467 | 30 | public function isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex) |
|
468 | { |
||
469 | 30 | $documentIndexOptions = $documentIndex['options']; |
|
470 | |||
471 | 30 | if ($mongoIndex['key'] != $documentIndex['keys']) { |
|
472 | 2 | return false; |
|
473 | } |
||
474 | |||
475 | 28 | if (empty($mongoIndex['sparse']) xor empty($documentIndexOptions['sparse'])) { |
|
476 | 2 | return false; |
|
477 | } |
||
478 | |||
479 | 26 | if (empty($mongoIndex['unique']) xor empty($documentIndexOptions['unique'])) { |
|
480 | 2 | return false; |
|
481 | } |
||
482 | |||
483 | 24 | if ( ! empty($mongoIndex['unique']) && empty($mongoIndex['dropDups']) && |
|
484 | 2 | ! empty($documentIndexOptions['unique']) && ! empty($documentIndexOptions['dropDups'])) { |
|
485 | |||
486 | 1 | return false; |
|
487 | } |
||
488 | |||
489 | 23 | foreach (array('bits', 'max', 'min') as $option) { |
|
490 | 23 | if (isset($mongoIndex[$option]) xor isset($documentIndexOptions[$option])) { |
|
491 | 6 | return false; |
|
492 | } |
||
493 | |||
494 | 21 | if (isset($mongoIndex[$option]) && isset($documentIndexOptions[$option]) && |
|
495 | 6 | $mongoIndex[$option] !== $documentIndexOptions[$option]) { |
|
496 | |||
497 | 3 | return false; |
|
498 | } |
||
499 | } |
||
500 | |||
501 | 14 | if (empty($mongoIndex['partialFilterExpression']) xor empty($documentIndexOptions['partialFilterExpression'])) { |
|
502 | 2 | return false; |
|
503 | } |
||
504 | |||
505 | 12 | if (isset($mongoIndex['partialFilterExpression']) && isset($documentIndexOptions['partialFilterExpression']) && |
|
506 | 2 | $mongoIndex['partialFilterExpression'] !== $documentIndexOptions['partialFilterExpression']) { |
|
507 | |||
508 | 1 | return false; |
|
509 | } |
||
510 | |||
511 | 11 | return true; |
|
512 | } |
||
513 | |||
514 | /** |
||
515 | * Ensure collections are sharded for all documents that can be loaded with the |
||
516 | * metadata factory. |
||
517 | * |
||
518 | * @param array $indexOptions Options for `ensureIndex` command. It's performed on an existing collections |
||
519 | * |
||
520 | * @throws MongoDBException |
||
521 | */ |
||
522 | public function ensureSharding(array $indexOptions = array()) |
||
523 | { |
||
524 | foreach ($this->metadataFactory->getAllMetadata() as $class) { |
||
525 | if ($class->isMappedSuperclass || !$class->isSharded()) { |
||
526 | continue; |
||
527 | } |
||
528 | |||
529 | $this->ensureDocumentSharding($class->name, $indexOptions); |
||
530 | } |
||
531 | } |
||
532 | |||
533 | /** |
||
534 | * Ensure sharding for collection by document name. |
||
535 | * |
||
536 | * @param string $documentName |
||
537 | * @param array $indexOptions Options for `ensureIndex` command. It's performed on an existing collections. |
||
538 | * |
||
539 | * @throws MongoDBException |
||
540 | */ |
||
541 | 3 | public function ensureDocumentSharding($documentName, array $indexOptions = array()) |
|
542 | { |
||
543 | 3 | $class = $this->dm->getClassMetadata($documentName); |
|
544 | 3 | if ( ! $class->isSharded()) { |
|
545 | return; |
||
546 | } |
||
547 | |||
548 | 3 | $this->enableShardingForDbByDocumentName($documentName); |
|
549 | |||
550 | 3 | $try = 0; |
|
551 | do { |
||
552 | 3 | $result = $this->runShardCollectionCommand($documentName); |
|
553 | 3 | $done = true; |
|
554 | |||
555 | // Need to check error message because MongoDB 3.0 does not return a code for this error |
||
556 | 3 | if ($result['ok'] != 1 && strpos($result['errmsg'], 'please create an index that starts') !== false) { |
|
557 | // The proposed key is not returned when using mongo-php-adapter with ext-mongodb. |
||
558 | // See https://github.com/mongodb/mongo-php-driver/issues/296 for details |
||
559 | if (isset($result['proposedKey'])) { |
||
560 | $key = $result['proposedKey']; |
||
561 | } else { |
||
562 | $key = $this->dm->getClassMetadata($documentName)->getShardKey()['keys']; |
||
563 | } |
||
564 | |||
565 | $this->dm->getDocumentCollection($documentName)->ensureIndex($key, $indexOptions); |
||
566 | $done = false; |
||
567 | $try++; |
||
568 | } |
||
569 | 3 | } while (! $done && $try < 2); |
|
570 | |||
571 | // Starting with MongoDB 3.2, this command returns code 20 when a collection is already sharded. |
||
572 | // For older MongoDB versions, check the error message |
||
573 | 3 | View Code Duplication | if ($result['ok'] == 1 || (isset($result['code']) && $result['code'] == 20) || $result['errmsg'] == 'already sharded') { |
574 | 2 | return; |
|
575 | } |
||
576 | |||
577 | 1 | throw MongoDBException::failedToEnsureDocumentSharding($documentName, $result['errmsg']); |
|
578 | } |
||
579 | |||
580 | /** |
||
581 | * Enable sharding for database which contains documents with given name. |
||
582 | * |
||
583 | * @param string $documentName |
||
584 | * |
||
585 | * @throws MongoDBException |
||
586 | */ |
||
587 | 6 | public function enableShardingForDbByDocumentName($documentName) |
|
588 | { |
||
589 | 6 | $dbName = $this->dm->getDocumentDatabase($documentName)->getName(); |
|
590 | 6 | $adminDb = $this->dm->getConnection()->selectDatabase('admin'); |
|
591 | 6 | $result = $adminDb->command(array('enableSharding' => $dbName)); |
|
592 | |||
593 | // Error code is only available with MongoDB 3.2. MongoDB 3.0 only returns a message |
||
594 | // Thus, check code if it exists and fall back on error message |
||
595 | 6 | View Code Duplication | if ($result['ok'] == 1 || (isset($result['code']) && $result['code'] == 23) || $result['errmsg'] == 'already enabled') { |
596 | 5 | return; |
|
597 | } |
||
598 | |||
599 | 1 | throw MongoDBException::failedToEnableSharding($dbName, $result['errmsg']); |
|
600 | } |
||
601 | |||
602 | /** |
||
603 | * @param $documentName |
||
604 | * |
||
605 | * @return array |
||
606 | */ |
||
607 | 3 | private function runShardCollectionCommand($documentName) |
|
608 | { |
||
609 | 3 | $class = $this->dm->getClassMetadata($documentName); |
|
610 | 3 | $dbName = $this->dm->getDocumentDatabase($documentName)->getName(); |
|
611 | 3 | $shardKey = $class->getShardKey(); |
|
612 | 3 | $adminDb = $this->dm->getConnection()->selectDatabase('admin'); |
|
613 | |||
614 | 3 | $result = $adminDb->command( |
|
615 | array( |
||
616 | 3 | 'shardCollection' => $dbName . '.' . $class->getCollection(), |
|
617 | 3 | 'key' => $shardKey['keys'] |
|
618 | ) |
||
619 | ); |
||
620 | |||
621 | 3 | return $result; |
|
622 | } |
||
623 | } |
||
624 |
If you suppress an error, we recommend checking for the error condition explicitly: