Updating an embedded subdocument in an array using doctrine and mongodb

I want to update (replace) a subdocument in an array of all documents that have this embedded subdocument.

My content example:

{ "_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" } ] } 

I want to find an object with a specific descriptor._id . I want to replace one sub-base with another (given the updated version of the old object, but not necessarily with the same properties).

Although this works well in 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 }) 

... and simple 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) ); 

... it does not work with 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(); 

... because it fails with the following error:

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

Therefore, I assume that Doctrine MongoDB ODM needs three parts in dot notation. A not very pleasant solution would be to sort through the properties of the subdocument and set them manually.

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

But this will only update existing and add new properties , but will not remove old / unnecessary properties from the subdocument.

Any ideas how to solve the problem:

  • with a simple request
  • when using Doctrine MongoDB ODM
  • without skew over subdocument array in php

I use:

  • Mongo-Server: 2.4.5
  • PHP: 5.4.16
  • PHP-Mongo-Driver: 1.4.1

Composer:

 "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" 
+4
source share
2 answers

This was a bug in ODM and should be fixed in PR # 661 . Please take a look and make sure that the revised test satisfies your use case.

Until the next beta ODM is marked, it will only be in the main branch; however, this should meet your requirement of version 1.0.*@dev .

+2
source

You can do this, but in a "difficult" way, if you use EmbedMany or ReferenceMany in Doctrine becames Doctrine ArrayCollection that implemented ArrayCollection contains () s (as explained here ) with PHP in_array () with the $ strict parameter set to true ... this way works with ReferenceMany, but not with EmbedMany!

Concept: ArrayCollection → array → ArrayCollection

In the definition of the 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); } 
+1
source

Source: https://habr.com/ru/post/1493715/


All Articles