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"