2013-07-26 131 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); }