Doctrine ODM flush() 使用唯一键

3
我使用Doctrine ODM来处理MongoDB。我有要保存的文档,这些文档会重复出现。我只需要每个事件的1个副本,所以我使用哈希唯一键来确保事件只有1个。
所以我执行几个 ->persist($document); 然后当我执行 ->flush(); 时,会出现异常: localhost:27017:E11000重复键错误索引:dbname.event.$eventKey_1 dup key:{: "keyValue"}
所有数据都未保存到MongoDB。所以问题是:是否有任何方法可以持久化唯一数据并忽略现有数据而不执行以下操作?
try {
    ->persist();
    ->flush();
} catch (\Exception $e) {}

每个文档都是如何处理的?

谢谢。

更新: 感谢您的回答和时间,但我已经找到确切的解决方案 :)

Mongo函数insert有一个选项"ordered:" https://docs.mongodb.org/manual/reference/method/db.collection.insert/ 它允许在出现错误后继续插入。

Doctrine使用Pecl扩展来操作Mongo。 doctrine flush()使用的是这个方法: http://www.php.net/manual/en/mongocollection.batchinsert.php 它有一个选项"continueOnError"

所以如果你按照这种方式做:

$documentManager->flush(null, ['continueOnError' => true]);

它将保存所有文档而不出现错误,并跳过所有带有错误的文档。虽然它会抛出 "\MongoDuplicateKeyException" 异常。所以你需要做的就是捕获这个异常并处理或者简单地忽略它(取决于你的需求)。

就像这样 :)


在持久化之前,您可以尝试查找数据库中是否已经存在类似的文档,如果存在,则只需链接到它而不是持久化它。 - Carlos Granados
是的,我考虑过这个问题,但我的想法并不是添加额外的查询。我认为应该有一些选项可以忽略这些异常情况。 - Immediate Right
3个回答

2
本地Doctrine方法不支持过滤唯一值 - 您需要自己完成此操作。要插入这些数据而不出现任何错误,您需要根据实体结构执行以下几个步骤:
  1. 查找具有您拥有的唯一键的所有现有实体

  2. 查找在您尝试持久化的实体之间重复的唯一键

  3. 用您找到的实体替换已经存在的实体

  4. 持久化并刷新

没有至少一个额外查询的机会可以做到这一点。如果您有现有实体的主键,您可以使用它们获取引用对象。但是,根据Doctrine文档,没有支持通过唯一键获取引用的功能:

http://doctrine-orm.readthedocs.org/en/latest/reference/limitations-and-known-issues.html#join-columns-with-non-primary-keys

不可能使用指向非主键的连接列。Doctrine 会认为这些是主键,并创建带有数据的延迟加载代理,这可能导致意外结果。出于性能原因,Doctrine 无法在运行时验证此设置的正确性,而只能通过“验证模式”命令进行验证。

1
我无法使 ['continueOnError' => true] 生效,所以采用了不同的解决方案:
$collection = $documentManager->getDocumentCollection(Content::class);
try {
    $collection->insertMany($arrayData, ['ordered' => false]);
} catch (BulkWriteException $exception) {
    // ignore duplicate key errors
    if (false === strpos($exception->getMessage(), 'E11000 duplicate key error')) {
        throw $exception;
    }
}

-2
你可以这样做:搜索你现有的ID,进行编辑并保存。
  • 使用Doctrine + Symfony:

    $Document = $EntityManager->getRepository("ExpBundle:Document")->find(123);
    $Document->setTitile("文档");  
    $EntityManager->flush();
    
  • 使用Doctrine:

    $Document = $EntityManager->find('Document', 1234);  
    $Document->setTitle("编辑");  
    $EntityManager->flush();
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接