Question

I'm playing with the UniqueIndex that I picked from the doc of Doctrine ODM and it seems I have a misanderstood of what it aims to do.

Indeed I have a Keyword Document mapped by Doctrine ODM :

Namespace App\Document;

use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;

/**
 * @ODM\Document
 * @ODM\UniqueIndex(keys={"name"="asc", "lang"="asc"})
 */
class Keyword {

    /** @ODM\Id(strategy="AUTO") */
    protected $id;

    /** @ODM\String */
    protected $name;

    /** @ODM\String */
    protected $lang;

    ....

As you can see the Document has an uniqueIndex on 2 keys (Name and Lang)

I have a all simple script which persist this Document

    ....
    ....
    $keyword=new \App\Document\Keyword();
    $keyword->setCreateDate(new \DateTime());
    $keyword->setLang("fr");
    $keyword->setLastParseDate(new \DateTime());
    $keyword->setName("test");

    $dm->persist($keyword);

    $dm->flush();

Now when i find from mongo shell, my data with the same pair Name/lang are replicated when they should be unique :

> db.Keyword.find()
{ "_id" : ObjectId("5171c72c6155795e47000000"), "name" : "test", "lang" : "fr", "createDate" : ISODate("2013-04-19T22:37:32Z"), "lastParseDate" : ISODate("2013-04-19T22:37:32Z") }
{ "_id" : ObjectId("5171c7366155796147000000"), "name" : "test", "lang" : "fr", "createDate" : ISODate("2013-04-19T22:37:42Z"), "lastParseDate" : ISODate("2013-04-19T22:37:42Z") }
{ "_id" : ObjectId("5171c7406155796447000000"), "name" : "test", "lang" : "fo", "createDate" : ISODate("2013-04-19T22:37:52Z"), "lastParseDate" : ISODate("2013-04-19T22:37:52Z") }
{ "_id" : ObjectId("5171c7fd615579a747000000"), "name" : "test", "lang" : "fo", "createDate" : ISODate("2013-04-19T22:41:01Z"), "lastParseDate" : ISODate("2013-04-19T22:41:01Z") }
{ "_id" : ObjectId("5171c7fe615579aa47000000"), "name" : "test", "lang" : "fo", "createDate" : ISODate("2013-04-19T22:41:02Z"), "lastParseDate" : ISODate("2013-04-19T22:41:02Z") }

My goal is to make the pair name/lang unique for persistence.

So I finally have two questions :

  • What UniqueIndex is made for ? (Because it doesnt prevent to replication)
  • Should I use a custom strategy that concatenates Name and Lang as an unique id ? Is it a common usage ?

EDIT :

Thanks to @gview advices I found that i didnt ensureIndexes. I fixed thanks to this link : http://www.testically.org/2011/08/25/using-a-unique-index-in-mongodb-with-doctrine-odm-and-symfony2/

But now instead of update my entry, it throws an error for duplicate entry. Should i use custom id as i said above ?

Was it helpful?

Solution 2

The index ensure only that the Documents don't get duplicated.

If you want to do the equivalent of a "REPLACE INTO", you should either:


Get the document if present, then set the values:

$keyword= $dm->findBy(array("name"=> $name, "lang"=> $lang));
if(!$keyword) {
    $keyword= new Keyword();
    $dm->persist($keyword);
}
$keyword->setCreateDate(new \DateTime());
$keyword->setLang("fr");
$keyword->setLastParseDate(new \DateTime());
$keyword->setName("test");

This will lead to 2 queries.


Or:

Do an upsert:

$dm->createQueryBuilder('Keyword')
   ->setNewObj(array(
       'lang' => 'fr',
       'name' => 'test',
       // ... other fields
   ))
   ->field('lang')->equals('fr')
   ->field('name')->equals('test')
   ->getQuery()
   ->execute();

This will update the doc if present, otherwise it will create a new Document.

However, the new document is created from the raw array, actually bypassing all the Doctrine events (like the @Timestampeble annotation).

So if the extra query isn't a problem, use the first method.

OTHER TIPS

Try to use ensureIndexes like this:

$dm = $this->get('doctrine_mongodb')->getManager();
$dm->getSchemaManager()->ensureIndexes();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top