如何使用mongodb从doctrine ODM中引用的文档数组中删除文档

如何使用mongodb从doctrine ODM中引用的文档数组中删除文档,mongodb,symfony,doctrine,doctrine-orm,Mongodb,Symfony,Doctrine,Doctrine Orm,我有一个php对象映射到一个具有结构的mongodb文档(称为节点) use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB; class Node{ /** * @MongoDB\Id */ protected $id; /** * @MongoDB\String */ protected $domain; /** * @MongoDB\Referenc

我有一个php对象映射到一个具有结构的mongodb文档(称为节点)

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

class Node{
    /**
    * @MongoDB\Id
    */
    protected $id;

    /**
    * @MongoDB\String
    */
    protected $domain;

    /**
    * @MongoDB\ReferenceMany(targetDocument="NodeItem",cascade=     
    * {"persist"},simple="true")
    */
    protected $items = array();

    //getter and setters below
}
还有一份参考文件名为NodeItem

class NodeItem {

  /**
  * @MongoDB\Id
  */
  protected $id;

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

  /**
   * @MongoDB\ReferenceOne(targetDocument="Node", cascade={"persist"},    
   *  simple="true")
   */
   protected Node;

   //setter and getters 
}
正如上面的注释所反映的,“Node”引用了存储在$items数组中的许多“NodeItems”,而“NodeItems”引用了一个“Node”。因此,这些是双向引用集合

我的问题是如何有效地从其集合中删除一些“NodeItem”文档(基于可用ID的数组),以便删除的NodeItem文档也从“Node”中的$items数组引用中删除(我想这就是我要求的级联删除?)

我编写了一个函数,其代码如下:

   $qb = $this->dm->createQueryBuilder('SomeBundleBundle:NodeItem');
    /*
     * deletes from NodeItem collection
     */
    foreach($NodeItemsArray as $itemId){
        $qb->remove()->field('id')->equals($itemId)->getQuery()->execute();
    }
但上述函数仅从NodeItem集合中删除文档,但不会删除'Node'的$items数组中的关联项。此外,注释中的{cascade:persist}似乎也没有帮助。该代码在symfony2框架中实现


谢谢你的帮助

ODM中的级联行为只受UnitOfWork操作的尊重。MongoDB本机不支持级联和触发器(但无论如何)。在您的情况下,查询生成器将构造并执行如下查询:

db.node_items.remove({"_id": ObjectId("...")})
根本不涉及UnitOfWork(不存在分阶段操作或冲洗),也不触发级联

另一方面,假设您有一个托管的
$nodeItem
对象。将其传递给
DocumentManager::remove()
将调用UnitOfWork,并导致使用
cascade=remove
cascade=ALL
映射的任何引用文档也被删除。当然,您必须调用
flush()
来执行MongoDB中的操作

根据当前代码,将级联的唯一操作是
DocumentManager::persist()
。在实践中,我假设您将创建一个节点,构造并向其中添加一些NodeItems,然后持久化该节点(允许自动持久化其项)

如果NodeItems只属于一个节点,您可能希望避免
cascade=REMOVE
,只需在删除
$nodeItem
本身之后,但在
刷新()之前,执行
$nodeItem->getNode()->getItems()->removeElement($nodeItem)


另外,我注意到您正在将集合字段初始化为数组。稍后,ODM将把这个字段作为
Doctrine\ODM\MongoDB\PersistentCollection
实例,这可能会导致歧义。作为最佳实践,您应该在构造函数中初始化
Doctrine\Common\Collections\ArrayCollection
等字段。这样,您总是可以期望它们是
集合
实例。

实现这一点的唯一方法是使用onRemove事件的侦听器

但是@jmikola提到,您必须使用$dm->remove()方法,而不是QueryBuilder(因为它还不支持事件)


因此,要删除该项,请执行以下操作:

//Get the NodeItem you want in the items array and delete it:
$items = $node->getItems();
$dm->remove($items->get(2)); //Remove the third item as an example

注册事件

class CascadeNodeDeleterListener {
        public function preRemove(LifecycleEventArgs $eventArgs) {

        $odm = $eventArgs->getDocumentManager(); /* @var $odm DocumentManager */
        $object = $eventArgs->getDocument();
        if($object instanceOf NodeItem) {
                $node = $object->getNode();
                $items = $node->getItems();
                $items->removeElement($object);
                $class = $dm->getClassMetadata(get_class($node));
                $dm->getUnitOfWork()->recomputeSingleDocumentChangeSet($class, $node);
            }

        }
}
services.yml
中:

 <service id="listener" class="CascadeNodeDeleterListener">
      <tag name="doctrine.common.event_listener" event="onRemove" />
 </service>

查看更多信息