【问题标题】:Update embedded subdocument in array with doctrine and mongodb使用教义和 mongodb 更新数组中的嵌入子文档
【发布时间】:2013-07-27 00:27:19
【问题描述】:

我想更新(替换)包含此特定嵌入子文档的所有文档数组中的子文档。

我的示例 content 对象是:

{
    "_id" : ObjectId("51f289e5345f9d10090022ef"),
    "title" : "This is a content",
    "descriptors" : [ 
        {
            "_id" : ObjectId("51f289e5345f9d10090022f4"),
            "name" : "This is a descriptor",
            "type" : "This is a property"
        },
        {
            "_id" : ObjectId("51f289e5345f9d10090022f0"),
            "name" : "This is another descriptor",
            "type" : "This is another property"
        }
    ]
}

我想找到具有特定descriptor._id 的对象。 我想用另一个子文档替换一个子文档(分别使用旧对象的更新版本,但不一定具有相同的属性)。

虽然这在RoboMongo / shell 中运行良好...

db.Content.update(
{
    'descriptors._id': ObjectId("51f289e5345f9d10090022f4")
},
{
    $set: {
        'descriptors.$': {
            "_id" : ObjectId("51f289e5345f9d10090022f4"),
            "name" : "This is the updated descriptor",
            "category" : "This is a new property"
        }
    }
},
{
    'multi': true
})

...和plain php methods...

$descriptor = array();
$descriptor['_id'] = new \MongoID('51f289e5345f9d10090022f4');
$descriptor['name'] = 'This is the updated descriptor';
$descriptor['category'] = 'This is a new property';

$mongo = new \Mongo('mongodb://localhost:27017');
$database = $mongo->selectDB('MyDatabase');

$output = $database->selectCollection('Content')->update(
    array('descriptors._id' => $descriptor['_id']),
    array('$set' => array('descriptors.$' => $descriptor)),
    array("multiple" => true)
);

...它不适用于Doctrine MongoDB ODM...

$descriptor = array();
$descriptor['_id'] = new \MongoID('51f289e5345f9d10090022f4');
$descriptor['name'] = 'This is the updated descriptor';
$descriptor['category'] = 'This is a new property';

$query = $dm->createQueryBuilder('Content')
->update()->multiple(true)
->field('descriptors._id')->equals($descriptor['_id'])
->field('descriptors.$')->set($descriptor)
->getQuery()->execute();

...因为它失败并出现以下错误:

注意:未定义的偏移量:C:\MyProject\vendor\doctrine\mongodb-odm\lib\Doctrine\ODM\MongoDB\Persisters\DocumentPersister.php 第 998 行中的 2

所以我假设,Doctrine MongoDB ODM 需要点符号中的三个部分。不太好的解决方案是遍历子文档的属性并手动设置它们。

$descriptor = array();
$descriptor['_id'] = new \MongoID('51f289e5345f9d10090022f4');
$descriptor['name'] = 'This is the updated descriptor';
$descriptor['category'] = 'This is a new property';

$query = $dm->createQueryBuilder('Content')
->update()->multiple(true)
->field('descriptors._id')->equals($descriptor['_id']);
foreach ($descriptor as $key => $value) {
    $query->field('descriptors.$.'.$key)->set($value);
}
$query->getQuery()->execute();

但这只会更新现有的添加新的属性,而不会从子文档中删除旧的/不必要的属性。 p>

关于如何解决问题的任何想法:

  • 用一个简单的查询
  • 在使用 Doctrine MongoDB ODM 时
  • 无需循环遍历 php 中的子文档数组

我正在使用:

  • Mongo 服务器:2.4.5
  • PHP:5.4.16
  • PHP-Mongo-驱动程序:1.4.1

作曲家:

"php": ">=5.3.3",
"symfony/symfony": "2.3.*",
"doctrine/orm": ">=2.2.3,<2.4-dev",
"doctrine/doctrine-bundle": "1.2.*",
"doctrine/mongodb-odm": "1.0.*@dev",
"doctrine/mongodb-odm-bundle": "3.0.*@dev"

【问题讨论】:

    标签: mongodb doctrine document


    【解决方案1】:

    这是 ODM 中的错误,应在 PR #661 中修复。请查看并确保修改后的测试满足您的用例。

    在标记 ODM 的下一个 beta 之前,它只会驻留在 master 分支中;但是,这应该符合您的 1.0.*@dev 版本要求。

    【讨论】:

      【解决方案2】:

      你可以做这件事,但以一种“不容易”的方式,因为当你在 Doctrine 中使用 EmbedManyReferenceMany 时,会变成一个实现 @987654322 的 Doctrine ArrayCollection 对象@ 方法与(如 here 解释)与 PHP in_array() 方法与 $strict 参数设置为 true... 这种方式适用于 ReferenceMany 但不适用于 EmbedMany !

      概念是:ArrayCollection -> 数组 -> ArrayCollection

      在 Document 的定义中 ...

      /**
       * @ODM\EmbedMany(targetDocument="EmbeddedElement")
       */
      private $elements = array();
      
      ...
      
      public function getElements() { return $this->elements; }
      public function setElements($elements) { $this->elements = $elements; }
      public function addElement(Element $element) { $this->elements[] = $element; }
      
      public function setElement(Element $element, $index=0)
      {
          $elementsArray = $this->elements->toArray();
      
          $elementsArray[$index] = $element;
      
          $this->elements = new ArrayCollection($elementsArray);
      }
      
      public function removeElement(Element $element)
      {
          $index = array_search($element, $this->elements->toArray(), $strict=false);
      
          if($index !== false)
              $this->elements->remove($index);
      }
      
      public function removeElementByIndex($index) { $this->elements->remove($index); }
      

      【讨论】:

        猜你喜欢
        • 2016-11-23
        • 1970-01-01
        • 1970-01-01
        • 2016-12-18
        • 1970-01-01
        • 1970-01-01
        • 2013-09-18
        • 2017-04-28
        • 2016-12-31
        相关资源
        最近更新 更多