Skip to content

Commit 0cd3c37

Browse files
authored
Merge pull request #1966 from alcaeus/fix-duplicate-embedded-index-name
Fix embedding documents containing named indexes
2 parents c7868c5 + 52d47d3 commit 0cd3c37

File tree

4 files changed

+119
-1
lines changed

4 files changed

+119
-1
lines changed

docs/en/reference/annotations-reference.rst

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,7 +794,8 @@ Optional attributes:
794794
respectively. Special index types (e.g. "2dsphere") should be specified as
795795
strings. This is required when `@Index`_ is used at the class level.
796796
-
797-
options - Options for creating the index
797+
options - Options for creating the index. Options are documented in the
798+
:ref:`indexes chapter <indees>`.
798799

799800
The ``keys`` and ``options`` attributes correspond to the arguments for
800801
`MongoCollection::createIndex() <http://php.net/manual/en/mongocollection.createindex.php>`_.
@@ -827,6 +828,15 @@ If you are creating a single-field index, you can simply specify an `@Index`_ or
827828
/** @Field(type="string") @UniqueIndex */
828829
private $username;
829830
831+
.. note::
832+
833+
If the ``name`` option is specified on an index in an embedded document, it
834+
will be prefixed with the embedded field path before creating the index.
835+
This is necessary to avoid index name conflict when the same document is
836+
embedded multiple times in a single collection. Prefixing of the index name
837+
can cause errors due to excessive index name length. In this case, try
838+
shortening the index name or embedded field path.
839+
830840
@Indexes
831841
--------
832842

docs/en/reference/indexes.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,15 @@ Also, for your convenience you can create the indexes for your mapped documents
335335
unless you specify a discriminator map for the :ref:`embed-one <embed_one>`
336336
or :ref:`embed-many <embed_many>` relationship.
337337

338+
.. note::
339+
340+
If the ``name`` option is specified on an index in an embedded document, it
341+
will be prefixed with the embedded field path before creating the index.
342+
This is necessary to avoid index name conflict when the same document is
343+
embedded multiple times in a single collection. Prefixing of the index name
344+
can cause errors due to excessive index name length. In this case, try
345+
shortening the index name or embedded field path.
346+
338347
Geospatial Indexing
339348
-------------------
340349

lib/Doctrine/ODM/MongoDB/SchemaManager.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
2323
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory;
2424
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo;
25+
use function sprintf;
2526

2627
class SchemaManager
2728
{
@@ -173,18 +174,25 @@ private function doGetDocumentIndexes($documentName, array &$visited)
173174
} else {
174175
continue;
175176
}
177+
176178
foreach ($possibleEmbeds as $embed) {
177179
if (isset($embeddedDocumentIndexes[$embed])) {
178180
$embeddedIndexes = $embeddedDocumentIndexes[$embed];
179181
} else {
180182
$embeddedIndexes = $this->doGetDocumentIndexes($embed, $visited);
181183
$embeddedDocumentIndexes[$embed] = $embeddedIndexes;
182184
}
185+
183186
foreach ($embeddedIndexes as $embeddedIndex) {
184187
foreach ($embeddedIndex['keys'] as $key => $value) {
185188
$embeddedIndex['keys'][$fieldMapping['name'] . '.' . $key] = $value;
186189
unset($embeddedIndex['keys'][$key]);
187190
}
191+
192+
if (isset($embeddedIndex['options']['name'])) {
193+
$embeddedIndex['options']['name'] = sprintf('%s_%s', $fieldMapping['name'], $embeddedIndex['options']['name']);
194+
}
195+
188196
$indexes[] = $embeddedIndex;
189197
}
190198
}
@@ -195,12 +203,15 @@ private function doGetDocumentIndexes($documentName, array &$visited)
195203
if ($key == $fieldMapping['name']) {
196204
$key = ClassMetadataInfo::getReferenceFieldName($fieldMapping['storeAs'], $key);
197205
}
206+
198207
$newKeys[$key] = $v;
199208
}
209+
200210
$indexes[$idx]['keys'] = $newKeys;
201211
}
202212
}
203213
}
214+
204215
return $indexes;
205216
}
206217

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
namespace Doctrine\ODM\MongoDB\Tests\Functional\Ticket;
4+
5+
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
6+
use Doctrine\ODM\MongoDB\Tests\BaseTest;
7+
use MongoResultException;
8+
9+
class GH1344Test extends BaseTest
10+
{
11+
public function testGeneratingIndexesDoesNotThrowException()
12+
{
13+
$indexes = $this->dm->getSchemaManager()->getDocumentIndexes(GH1344Main::class);
14+
self::assertCount(4, $indexes);
15+
self::assertSame('embedded1_embedded', $indexes[0]['options']['name']);
16+
self::assertSame('embedded1_embedded_nested', $indexes[1]['options']['name']);
17+
self::assertSame('embedded2_embedded', $indexes[2]['options']['name']);
18+
self::assertSame('embedded2_embedded_nested', $indexes[3]['options']['name']);
19+
20+
$this->dm->getSchemaManager()->ensureDocumentIndexes(GH1344Main::class);
21+
}
22+
23+
public function testGeneratingIndexesWithTooLongIndexNameThrowsException()
24+
{
25+
// Ensure that at least the beginning of the index name is contained in
26+
// the exception message. This can vary between driver/server versions.
27+
$this->expectException(MongoResultException::class);
28+
$this->expectExceptionMessageRegExp('#GH1344TooLongIndexName.\$embedded1_this_is_a_really_long_name_that#');
29+
30+
$this->dm->getSchemaManager()->ensureDocumentIndexes(GH1344TooLongIndexName::class);
31+
}
32+
}
33+
34+
/** @ODM\Document */
35+
class GH1344Main
36+
{
37+
/** @ODM\Id */
38+
public $id;
39+
40+
/** @ODM\EmbedOne(targetDocument=GH1344Embedded::class) */
41+
public $embedded1;
42+
43+
/** @ODM\EmbedOne(targetDocument=GH1344Embedded::class) */
44+
public $embedded2;
45+
}
46+
47+
/**
48+
* @ODM\EmbeddedDocument
49+
* @ODM\Index(keys={"property"="asc"}, name="embedded")
50+
*/
51+
class GH1344Embedded
52+
{
53+
/** @ODM\Field */
54+
public $property;
55+
56+
/** @ODM\EmbedOne(targetDocument=GH1344EmbeddedNested::class) */
57+
public $embedded;
58+
}
59+
60+
/**
61+
* @ODM\EmbeddedDocument
62+
* @ODM\Index(keys={"property"="asc"}, name="nested")
63+
*/
64+
class GH1344EmbeddedNested
65+
{
66+
/** @ODM\Field */
67+
public $property;
68+
}
69+
70+
/** @ODM\Document */
71+
class GH1344TooLongIndexName
72+
{
73+
/** @ODM\Id */
74+
public $id;
75+
76+
/** @ODM\EmbedOne(targetDocument=GH1344TooLongIndexNameEmbedded::class) */
77+
public $embedded1;
78+
}
79+
80+
/**
81+
* @ODM\EmbeddedDocument
82+
* @ODM\Index(keys={"property"="asc"}, name="this_is_a_really_long_name_that_will_cause_problems_for_whoever_tries_to_use_it_whether_in_an_embedded_field_or_not")
83+
*/
84+
class GH1344TooLongIndexNameEmbedded
85+
{
86+
/** @ODM\Field */
87+
public $property;
88+
}

0 commit comments

Comments
 (0)