mongodb-odm: Wrong mapping of $id field.

I think this is a wrong behaviour about mapping field in an embedded document. I’m using version: 3.1.0

This is my example:

Main document

/**
 * @ODM\Document
 */
class Main
{
    /**
     * @ODM\Id
     */
    private $id;

    /**
     * @ODM\EmbedMany(targetDocument="Embedded")
     */
    protected $embedded;
}

Embedded document

/**
 * @ODM\EmbeddedDocument
 */
class Embedded
{
    /**
     * @ODM\Field(type="string")
     */
    protected $id;

    /**
     * @ODM\Field(type="boolean")
     */
    protected $active;
}

In the embedded document there is a field called $id that is a string type, not @ODM\Id.

The problem comes when i try to query that field.

$qb
      ->field('embedded')->elemMatch($qb->expr()
          ->field('id')->equals(1)
     )
;

$qb->getQuery()->execute();

This will result in this query: "embedded":{"$elemMatch":{"_id":{"$id":"5a0d7aee26567e3c36166391"}}}

But i suppose to have: "embedded":{"$elemMatch":{"id":1}}

So I investigate in the mapField function and i noticed that:

Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo line 1063: public function mapField(array $mapping)

  if (isset($mapping['id']) && $mapping['id'] === true) {
            $mapping['name'] = '_id';
            $this->identifier = $mapping['fieldName'];
            ...
 }

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 16 (8 by maintainers)

Most upvoted comments

@iskyd @webdevilopers #2299 fixes the original issue where the wrong ClassMetadata instance was used. I’ll revisit the problem of conflicting field names separately, but the PR should fix most issues when using an Expr on an association with its own metadata.

I will have to correct myself on the last comment. Indeed our solution on order to keep the domain model clean and stay with our internal “$id” property we can do this:

        <id field-name="surrogateId" />
        <field field-name="id" type="string" />

@KarunaGovind, @webdevilopers

In ODM, both field name and name (aka database name) are immediately reserved and required to be unique. There may not be a field with a name equal to another fields fieldName attribute. The reason for this is because it’s possible for you to create a query like this:

$builder->field('someReference')->references($someObject);

In this case, we wouldn’t want you to have to know how exactly someReference is mapped (as ID, DBRef or ref). Also, you shouldn’t have to know what identifier $someObject uses. So, we use mapping information as well as the someReference fieldName to get to this query object:

{
    "someReference.$id": ObjectId("123456..."),
}

This replicates behaviour from ORM, which is why it was built this way. Unfortunately, in your case this means that you’ll have to find a different fieldName for your id field unless you can change the schema (which isn’t always possible).

In the original example, this is a bit different: the query expression contains metadata for the original it was built for, which is why the id field is changed to _id. That’s why changing the classMetadata instance in the expression object yields the correct result. One possible fix here is adding an optional $field argument to expr() which would allow you to get an expression based on the metadata of a nested field. I’ll see if we can fix this in 1.2 or 1.3.