简体   繁体   English

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

[英]How to delete document from a referenced array of documents in doctrine ODM with mongodb

I have a php object mapping to a mongodb document(called Node) with a structure 我有一个php对象映射到具有结构的mongodb文档(称为Node)

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
}

And a referenced document called, NodeItem, 一个名为NodeItem的引用文档,

class NodeItem {

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

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

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

   //setter and getters 
}

As reflected by the annotations above 'Node' references MANY 'NodeItems' stored in a $items array and 'NodeItems' references ONE 'Node'. 正如“节点”上面的注释所反映的那样,引用存储在$ items数组中的许多“NodeItems”和“NodeItems”引用了“节点”。 So those are bi-directional referenced collections. 那些是双向引用的集合。

My Question is how to effectively delete a few 'NodeItem' documents from its collection (based on the array of available ids), so that the deleted NodeItem documents are also deleted from the $items array references in 'Node' (cascaded delete I think is what I am asking for?). 我的问题是如何有效地从其集合中删除一些“NodeItem”文档(基于可用ID数组),以便删除的NodeItem文档也从'Node'中的$ items数组引用中删除(我认为级联删除)是我要求的?)。

I wrote a function that has code like this : 我写了一个函数,其代码如下:

   $qb = $this->dm->createQueryBuilder('SomeBundleBundle:NodeItem');
    /*
     * deletes from NodeItem collection
     */
    foreach($NodeItemsArray as $itemId){
        $qb->remove()->field('id')->equals($itemId)->getQuery()->execute();
    }

But the above function only deletes the documents from NodeItem collection, but the associated items in the $items array of 'Node' are not deleted. 但是上面的函数只删除NodeItem集合中的文档,但是'Node'的$ items数组中的相关项不会被删除。 Also, the {cascade:persist} in the annotations doesn't seem to help. 此外,注释中的{cascade:persist}似乎没有帮助。 The code is implemented in Symfony 2 framework 代码在Symfony 2框架中实现

Some help is appreciated ! 一些帮助表示赞赏!

The only way to achieve that is with a listener on the onRemove event. 实现这一目标的唯一方法是使用onRemove事件上的侦听器。

But has mentioned by @jmikola, you'll have to use the $dm->remove() method, and not the QueryBuilder (since it doesn't support events yet). 但是@jmikola已经提到过,你必须使用$ dm-> remove()方法,而不是QueryBuilder(因为它还不支持事件)。


so, to delete the Item do: 所以, 要删除Item do:

//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

And register the event : 注册活动

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);
            }

        }
}

In services.yml : services.yml

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

See Doctrine ODM Events from more info. 请参阅更多信息,了解Doctrine ODM Events

Cascade behavior in ODM is only respected by UnitOfWork operations. ODM中的级联行为仅受UnitOfWork操作的尊重。 MongoDB does not natively support cascades and triggers (yet, anyway). MongoDB本身不支持级联和触发器(无论如何)。 In your case, query builder will construct and execute a query like the following: 在您的情况下,查询构建器将构造并执行如下查询:

db.node_items.remove({"_id": ObjectId("...")})

UnitOfWork is not involved at all (there are no staged operations or flushing) and no cascade is triggered. UnitOfWork根本不涉及(没有分阶段操作或刷新)并且没有触发级联。

On the other hand, say you had a managed $nodeItem object. 另一方面,假设您有一个托管的$nodeItem对象。 Passing it to DocumentManager::remove() would invoke UnitOfWork and cause any referenced documents mapped with cascade=REMOVE or cascade=ALL to also be removed. 将它传递给DocumentManager::remove()将调用UnitOfWork并导致任何使用cascade=REMOVEcascade=ALL映射的引用文档也将被删除。 Of course, you'd have to call flush() to execute the operations in MongoDB. 当然,您必须调用flush()来执行MongoDB中的操作。

Based on your current code, the only operation that will be cascaded is DocumentManager::persist() . 根据您当前的代码,将级联的唯一操作是DocumentManager::persist() In practice, I assume you'd create a Node, construct and add a few NodeItems to it, and persist the Node (allowing its items to be persisted automatically). 在实践中,我假设您创建一个Node,构造并向其添加一些NodeItem,并持久化Node(允许其项自动保留)。

If NodeItems only ever belong to a single Node, you may want to avoid cascade=REMOVE and simply do $nodeItem->getNode()->getItems()->removeElement($nodeItem) after you remove the $nodeItem itself, but before flush() . 如果NodeItems只属于一个节点,你可能想要避免cascade=REMOVE并且在删除$nodeItem本身之后只需要执行$nodeItem->getNode()->getItems()->removeElement($nodeItem) flush()

Also, I noticed you're initializing your collection field to an array. 另外,我注意到你正在将你的集合字段初始化为一个数组。 Later on, ODM is going to hydrate this field as a Doctrine\\ODM\\MongoDB\\PersistentCollection instance, which could lead to ambiguity. 稍后,ODM将把这个字段保存为Doctrine\\ODM\\MongoDB\\PersistentCollection实例,这可能导致歧义。 As a best practice, you should initialize such fields as Doctrine\\Common\\Collections\\ArrayCollection in your constructor. 作为最佳实践,您应该在构造函数中初始化诸如Doctrine\\Common\\Collections\\ArrayCollection类的字段。 That way, you can always expect them to be Collection instances. 这样,您始终可以期望它们是Collection实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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