MongoDB插入含计算值的数组对象问题(Java/Spring环境)
问题:MongoDB中插入含计算值的数组元素未解析为实际值
环境与场景
基于Java 11、Spring 2.7、Mongo Atlas 5.0实现资金分配系统,原始文档结构如下:
[ { "_id": "id_1", "detail": { "amount": 500.00, "units": 50 }, "users": [ { "user": 1, "name": "User 1" }, { "user": 2, "name": "User 2" } ], "payments": [] }, { "_id": "id_2", "detail": { "amount": 200.00, "units": 50 }, "users": [], "payments": [] } ]
期望效果
需要向payments数组插入包含计算值的对象,规则为:
payment_code为指定值distributed根据users数组是否为空返回布尔值the_amount为detail.amount乘以指定百分比
传入20%(0.2)时,期望结果:
[ { "_id": "id_1", "detail": { "amount": 500.00, "units": 50 }, "users": [ { "user": 1, "name": "User 1" }, { "user": 2, "name": "User 2" } ], "payments": [ { "payment_code": 1, "distributed": true, "the_amount": 100.0 } ] }, { "_id": "id_2", "detail": { "amount": 200.00, "units": 50 }, "users": [], "payments": [ { "payment_code": 1, "distributed": false, "the_amount": 40.0 } ] } ]
当前问题
使用Spring Mongo API执行更新后,计算操作符未解析为实际值,而是以操作符对象的形式存入文档,结果如下:
{ ..., "payments": [ { "payment_code": 1234, "distributed": { "$gt": [ { "$size": "users" }, 0 ] }, "the_amount": { "$multiply": [ "detail.amount", "0.2" ] } } ] }
使用的Spring代码示例:
mongoTemplate.bulkOps(BulkMode.UNORDERED, "theCollection").updateMulti( new Query(...), new Update().set("payments.0.payment_code", 1234) .set("payments.0.distributed", ComparisonOperators.valueOf(Size.lengthOfArray("users")).greaterThanValue(0l)) .set("payments.0.the_amount", ArithmeticOperators.Multiply.valueOf("detail.amount").multiplyBy(new BigDecimal("0.2")) ).execute();
解决方案
问题出在直接使用Update.set方法会将操作符对象作为字面量存储,而非让MongoDB执行计算。需要使用聚合管道式更新(MongoDB 4.2+支持,Mongo Atlas 5.0满足要求),让MongoDB解析并执行表达式计算。
正确代码实现
使用Spring Data MongoDB的AggregationUpdate构建聚合管道更新:
// 构建聚合更新操作 AggregationUpdate update = AggregationUpdate.update() // 向payments数组push新对象 .push("payments") .withValue(Fields.fields( Field.field("payment_code", 1234), // 计算distributed:判断users数组长度是否大于0 Field.field("distributed", ComparisonOperators.Gt.valueOf(Size.lengthOfArray("$users")).greaterThanValue(0)), // 计算the_amount:detail.amount乘以百分比 Field.field("the_amount", ArithmeticOperators.Multiply.valueOf("$detail.amount").multiplyBy(new BigDecimal("0.2"))) )); // 执行批量更新 mongoTemplate.bulkOps(BulkMode.UNORDERED, "theCollection") .updateMulti(new Query(), update) .execute();
原理说明
AggregationUpdate会将更新逻辑转换为MongoDB聚合管道,管道中的表达式会被MongoDB执行,将计算结果作为字段值存入文档- 字段引用需要添加
$前缀(如$users、$detail.amount),表示引用文档中的对应字段 push操作符用于向数组添加新元素,结合聚合表达式就能生成包含计算值的对象
内容的提问来源于stack exchange,提问作者Hugo Serrano
相关产品推荐
相关产品推荐

