使用Doctrine MongoDB ODM进行原子操作

时间:2012-07-31 06:44:50

标签: mongodb symfony doctrine

我似乎无法使用以下查询。基本上,我正在尝试将消息文档添加到对话文档中,如下所示:

public function reply($conversationId, Message $message, $flush = true)
{            
    $this->dm->createQueryBuilder($this->class)
        ->field('archivers')->unsetField()
        ->field('repliedBy')->set($message->getUserId())
        ->field('repliedBody')->set($message->getBody())
        ->field('repliedAt')->set(new \DateTime())
        ->field('modifiedAt')->set(new \DateTime())
        ->field('messages')->push($message)
        ->field('id')->equals(new \MongoId($conversationId))
        ->getQuery()
        ->execute();

    if ($flush) {
        $this->dm->flush();
    }
}

此回复方法以两种方式调用。首先是用户通过html表单发布消息,然后是由Android应用程序发出的REST调用。表单有效但REST调用失败(其余实现使用带有FOSRestBundle btw的JMSSerializerBundle)...

我已经验证了代码被调用,并且传递给方法的参数在两种情况下都是有效的,但由于某种原因,UnitOfWork.php中的commit()调用忽略了对文档的更改。请参阅line 413了解我的意思。

有人知道为什么会这样吗?

以下是我尝试的其他方法:

首先,我添加了一个update()调用失败,其中包含“Catchable Fatal Error:类的对象...无法转换为/vendor/bundles/Symfony/Bundle/DoctrineMongoDBBundle/Logger/DoctrineMongoDBLogger.php行中的字符串280" 。

public function reply($conversationId, Message $message, $flush = true)
{            
    $this->dm->createQueryBuilder($this->class)
        ->update()
        ->field('archivers')->unsetField()
        ->field('repliedBy')->set($message->getUserId())
        ->field('repliedBody')->set($message->getBody())
        ->field('repliedAt')->set(new \DateTime())
        ->field('modifiedAt')->set(new \DateTime())
        ->field('messages')->push($message)
        ->field('id')->equals(new \MongoId($conversationId))
        ->getQuery()
        ->execute();

    if ($flush) {
        $this->dm->flush();
    }
}

我尝试的第二种方法是推送数组而不是对象:

public function reply($conversationId, Message $message)
{            
    $this->dm->createQueryBuilder($this->class)
        ->update()
        ->field('archivers')->unsetField()
        ->field('repliedBy')->set($message->getUserId())
        ->field('repliedBody')->set($message->getBody())
        ->field('repliedAt')->set(new \DateTime())
        ->field('modifiedAt')->set(new \DateTime())
        ->field('messages')->push(array(
            '_id' => new \MongoId(),
            'userId' => $message->getuserId(),
            'body' => $message->getBody(),
            'createdAt' => new \DateTime(),
            'modifiedAt' => new \DateTime(),
        ))
        ->field('id')->equals(new \MongoId($conversationId))
        ->getQuery() 
        ->execute();

    $this->dm->flush();
}

在调用flush()方法之前,它可以正常工作。 flush()导致重复的对象被推送。所以我在对话中得到了同一个消息的两个副本(注释flush()解决了问题但是应用程序在其他类中有多个flush()调用)。

另一个使用对象失败的推送查询:

public function archive($conversationId, $userId)
{

    $userStamp = new UserStamp();
    $userStamp->setUserId($userId);

    $this->dm->createQueryBuilder($this->class)
        ->update()
        ->field('archivers')->push($userStamp)
        ->field('modifiedAt')->set(new \DateTime())
        ->field('id')->equals(new \MongoId($conversationId))
        ->getQuery()
        ->execute();
}

如果删除了push()调用,一切正常。

此时仍然停滞不前。

2 个答案:

答案 0 :(得分:3)

查询构建器通常用于对MongoDB执行多文档或命令查询,后者绕过文档管理。唯一的例外是如果您正在执行水合查找查询,query builder documentation中对此进行了描述。您上面的示例等同于:

$collection->update(
    ['_id' => new \MongoId($conversationId)],
    [
        '$set' => [
            'repliedBy' => $message->getUserId(),
            'repliedBody' => $message->getbody(),
            'repliedAt' => new \MongoDate(),
            'modifiedAt' => new \MongoDate(),
        ],
        '$push' => ['messages' => $message],
    ],
    ['multiple' => false]
);

请注意,您的字段映射将被使用(例如,Datetime将成为MongoDate),但没有要使用此查询管理的文档。

答案 1 :(得分:0)

如果在更新期间推送对象,请在php中使用\ stdClass。 快速示例:

public function reply($conversationId, Message $message)
{      
       $object = new \stdClass();
       $object->_id = new \MongoId();
       $object->userId = $message->getuserId();
       $object->body = $message->getBody();
       $object->createdAt = new \MongoDate();
       $object->modifiedAt = new \MongoDate();

       $this->dm->createQueryBuilder($this->class)
        ->update()
        ->field('archivers')->unsetField()
        ->field('repliedBy')->set($message->getUserId())
        ->field('repliedBody')->set($message->getBody())
        ->field('repliedAt')->set(new \DateTime())
        ->field('modifiedAt')->set(new \DateTime())
        ->field('messages')->push()
        ->field('id')->equals(new \MongoId($conversationId))
        ->getQuery() 
        ->execute();

        $this->dm->flush();
}