简体   繁体   中英

Error persisting data with doctrine-mongodb EmbedMany strategy addToSet

i'm having trouble persisting data using EmbedMany and strategy addToSet .

Reference:

https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/2.2/reference/storage-strategies.html https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/2.2/reference/embedded-mapping.html#embed_many

When adding more than one Consumer to the embedded collection, the data persistent is not coherent, and also the strategy addToSet that prevents duplicates does not work properly.

The main reason is to append data to consumers embedded collection without duplicates

Example:

consumers is empty [ ]

  1. add consumer "CRM" -> consumers = [{'_id': "CRM", "consumer": "CRM"}]
  2. add consumer "HS" -> consumers = [{'_id': "CRM", "consumer": "CRM"}, {'_id': "HS", "consumer": "HS"}]
  3. adding again any of those consumers wont add new data to consumers embedded collection

The classes that i'm using:

Doctrine Mapping

<!-- DomainObjectConsumer.mongodb.xml-->
<doctrine-mongo-mapping xmlns="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xsi:schemaLocation="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping
                        https://doctrine-project.org/schemas/odm/doctrine-mongo-mapping.xsd">
    <document name="DomainObjectConsumer">
        <id strategy="NONE"/>
        <field  name="consumer" field-name="consumer" type="string"/>
    </document>

</doctrine-mongo-mapping>
<!-- DomainObjectLog.mongodb.xml-->
<doctrine-mongo-mapping xmlns="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xsi:schemaLocation="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping
                        https://doctrine-project.org/schemas/odm/doctrine-mongo-mapping.xsd">
    <document
            name="DomainObjectLog"
            collection="domain_object_log"
            repository-class="DomainObjectLogRepository">
        <id strategy="auto"/>
        <embed-many field="consumers"
                    strategy="addToSet"
                    target-document="DomainObjectConsumer"/>
    </document>

</doctrine-mongo-mapping>

Domain Layer

<?php 

class DomainObjectLog
{
    /** @var string|null */
    private $id;

    /**
     * @var ArrayCollection
     */
    private $consumers;

    public function __construct(?string $consumer) 
    {
        $this->consumers = new ArrayCollection();
        if ($consumer) {
            $objectConsumer = new DomainObjectConsumer($consumer);
            $this->addConsumer($objectConsumer);
        }
    }

    /**
     * @param DomainObjectConsumer $consumer
     * @return DomainObjectLog
     */
    public function addConsumer(DomainObjectConsumer $consumer): DomainObjectLog
    {
        $this->consumers->add($consumer);
        return $this;
    }

    /** getters & setters **/
}
<?php

class DomainObjectConsumer
{
    /** @var string|null */
    private $id;

    /**
     * @var string
     */
    private $consumer;

    public function __construct(string $consumer)
    {
        $this->id = $consumer; // _id could make objects different ?
        $this->consumer = $consumer;
    }

    /**
     * @param string $consumer
     * @return DomainObjectConsumer
     */
    public function setConsumer(string $consumer): DomainObjectConsumer
    {
        $this->consumer = $consumer;
        return $this;
    }

    /** getters & setters **/
}

Use Case scenario: Save

<?php

class SaveDomainObjectLog
{

    public function save(string $uuid, ?string $consumer)
    {
        $objectLog = $this->objectLogRepository->getByUUID($uuid);
        if (!$objectLog) {
            $objectLog = new DomainObjectLog($consumer);
        } else {
            if (!is_null($consumer)) {
                $objectConsumer = new DomainObjectConsumer($consumer);
                $objectLog->addConsumer($objectConsumer);
            }
        }

        $this->objectLogRepository->saveDomainObjectLog($objectLog);
    }

}

Persistence layer is just saving the object $objectLog as is.

The behaviour goes like this:

first call to save with parameter consumer = CRM:

  • field consumers: [{"_id": "CRM", "consumer": "CRM"}]

second call to save with parameter consumer = CRM:

  • field consumers: [{"_id": "CRM", "consumer": "CRM"}, {"consumer": "CRM"}]

third call to save with parameter consumer = CRM:

  • field consumers: [{"_id": "CRM", "consumer": "CRM"}, {"consumer": "CRM"}, {"consumer": "CRM"}]

The expected result should be just: [{"_id": "CRM", "consumer": "CRM"}] after any call with the same consumer name.

Is there any reason why this is happening?

Why the persisted data is different?

and why addToSet is not working removing duplicates ?

Thanks in advance.

EDIT 1:

Traces from doctrine:

"update":"domain_object_log" .... "u":{"$set":{"consumers.1.consumer":"CRM"}}
"update":"domain_object_log" .... "u":{"$addToSet":{"consumers":{"$each":[{"_id":"CRM","consumer":"CRM"}]}}}

Expected behaviuor:

db.domain_object_log.updateOne({
   "uuid":"12345678-1234-1234-1234-123f56780054"
},
{
   "$addToSet":{
      "consumers":{
         "_id":"CRM",
         "consumer":"CRM"
      }
   }
})

Found the solution:

The embedded document must be declared also as embedded in the xml file. This way you can remove the Id field.

<!-- DomainObjectConsumer.mongodb.xml-->
<doctrine-mongo-mapping xmlns="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xsi:schemaLocation="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping
                        https://doctrine-project.org/schemas/odm/doctrine-mongo-mapping.xsd">
    <embedded-document name="DomainObjectConsumer">
        <field  name="consumer" field-name="consumer" type="string"/>
    </embedded-document>

</doctrine-mongo-mapping>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM