如何在Doctrine ODM中使用文档自身其他字段值更新指定字段
问题原因
普通的Doctrine ODM set() 方法仅支持写入固定字面量,不会将你传入的$rate.total这类字符串识别为文档字段引用,因此无法实现同文档字段间的运算更新。
解决方案
MongoDB 4.2及以上版本支持使用聚合管道执行更新操作,可以直接引用文档现有字段做运算,Doctrine ODM 2.0及以上版本已内置对该特性的支持,你可以通过pipe()方法实现需求,修正后的代码如下:
public function updateCountAndRate(string $service, string $expert, float $rate, bool $comment = false) { $rateKey = (int)round($rate); $queryBuilder = $this->createQueryBuilder(); // 普通增量更新逻辑不变 $queryBuilder->field(sprintf('rate.counts.%d', $rateKey))->inc(1); $queryBuilder->field('rate.total')->inc(1); if ($comment) { $queryBuilder->field('comments')->inc(1); } // 聚合管道实现字段间运算更新平均值 $queryBuilder->pipe([ '$set' => [ 'rate.average' => [ '$divide' => [ [ '$add' => [ ['$multiply' => ['$rate.average', '$rate.total']], $rate ] ], ['$add' => ['$rate.total', 1]] ] ] ] ]); $queryBuilder->updateOne() ->getQuery() ->execute(); }
注意事项
- 该方案要求MongoDB服务端版本 >= 4.2,Doctrine ODM版本 >= 2.0
- 整个更新操作是原子性的,不存在并发更新导致的数据不一致问题
- 如果你使用的是不支持管道更新的旧版本,只能先查询出对应文档,在PHP端计算出新的平均值后再执行更新,这种情况建议配合乐观锁避免并发覆盖问题
内容的提问来源于stack exchange,提问作者Hamid
相关产品推荐
相关产品推荐

