2013-07-26 95 views
4

我想要更新(替换)所有文档的数组中的子文档,这些文档具有此特定的嵌入子文档。用doctrine和mongodb更新嵌入的子文档数组

我的样品内容对象是:

{ 
    "_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 /壳...

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

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

Notice: Undefined offset: 2 in C:\MyProject\vendor\doctrine\mongodb-odm\lib\Doctrine\ODM\MongoDB\Persisters\DocumentPersister.php line 998

所以,我认为,这主义的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(); 

但这只会现有更新添加新的特性,但不会从子文档中删除旧的/不必要的性能

任何想法如何解决这个问题:

  • 用一个简单的查询
  • 同时使用 主义的MongoDB ODM
  • 没有在PHP循环子文档阵在

我正在使用:

  • 蒙戈 - 服务器:2.4.5
  • PHP:5.4.16
  • PHP-蒙戈驱动程序: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" 

回答

2

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

直到ODM的下一个测试版被标记为止,这将只存在于主分支中;但是,这应该符合您的1.0.*@dev版本要求。

1

你可以做这件事情,但在“不容易”的方式,当您使用EmbedManyReferenceMany教义becames一个Doctrine ArrayCollection对象与执行的ArrayCollection contains()方法(如解释here)与PHP in_array()法becuse $严格参数设置为true ...这种方式适用于ReferenceMany,但不适用于EmbedMany!

的概念是:的ArrayCollection - >阵列 - >的ArrayCollection

在文献 的定义...

/** 
* @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); }