4

この特定の埋め込みサブドキュメントを持つすべてのドキュメントの配列内のサブドキュメントを更新 (置換) したいと考えています。

私のサンプルコンテンツオブジェクトは次のとおりです。

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

...そしてプレーンなphpメソッドで...

$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にはドット表記の 3 つの部分が必要だと思います。サブドキュメントのプロパティを反復処理して手動で設定するのは、あまり良くない解決策です。

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

ただし、これは既存のプロパティを更新して新しいプロパティを追加するだけで、古い/不要なプロパティをサブドキュメントから削除しません。

問題を解決する方法についてのアイデア:

  • 簡単なクエリで
  • Doctrine 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"
4

2 に答える 2

1

これを行うことはできますが、「簡単ではない」方法で行うことができます。これは、Doctrine でEmbedManyまたはReferenceManyを使用すると、 ArrayCollection contains()メソッドを実装したDoctrine ArrayCollectionオブジェクトになるためです(ここで説明されているよう) 。$strictパラメータを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); }
于 2016-01-22T14:35:32.427 に答える