MongoOperations 和 ReactiveMongoOperations 暴露的更新方法也通过 AggregationUpdate 接受一个聚合管道。使用 AggregationUpdate 可以在更新操作中利用 MongoDB 4.2 聚合。在更新中使用聚合允许通过用单个操作表达多个阶段和多个条件来更新一个或多个字段。
更新可包括以下阶段:
AggregationUpdate.set(…).toValue(…) → $set : { … }
AggregationUpdate.unset(…) → $unset : [ … ]
AggregationUpdate.replaceWith(…) → $replaceWith : { … }
本示例通过向 MongoDB 插入三个员工 Worker 文档,包含姓名、年龄和截止本年薪水。然后通过聚合管道计算每人的平均薪水,最后根据薪水计算它们薪水等级。
(1)工人实体 Worker 代码
package com.hxstrive.springdata.mongodb.entity; import lombok.Builder; import lombok.Data; import lombok.ToString; import java.util.List; /** * 工人实体 * @author hxstrive.com 2022/12/29 */ @Data @Builder @ToString public class Worker { /** 工人编号 */ private String id; /** 姓名 */ private String name; /** 年龄 */ private int age; /** 本年度薪水 */ private List<Float> salarys; }
(2)客户端,通过 MongoTemplate 实现聚合管道计算平均值等。
package com.hxstrive.springdata.mongodb; import com.hxstrive.springdata.mongodb.entity.Worker; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.aggregation.*; import java.util.Arrays; @SpringBootTest class AggregationUpdateTest { @Autowired private MongoTemplate mongoTemplate; @Test void contextLoads() { // 删除集合 mongoTemplate.dropCollection(Worker.class); // 准备数据 // 插入三个工人数据,以及本年度前四个月的薪水 mongoTemplate.insert(Worker.builder().name("Joe").age(28) .salarys(Arrays.asList(5050f, 4980f, 5200f, 5150f)).build()); mongoTemplate.insert(Worker.builder().name("Tom").age(30) .salarys(Arrays.asList(7000f, 6900f, 7200f, 8100f)).build()); mongoTemplate.insert(Worker.builder().name("Helen").age(31) .salarys(Arrays.asList(5500f, 4900f, 6200f, 5300f)).build()); // 通过聚合运算计算工人本年度前四个月的平均薪水 // 以及根据平均薪水判断工人薪水标准 AggregationUpdate update = Aggregation.newUpdate() .set("average").toValue(ArithmeticOperators.valueOf("salarys").avg()) .set("grade").toValue(ConditionalOperators.switchCases( // 根据前面计算的平均值判断标准 ConditionalOperators.Switch.CaseOperator.when( ComparisonOperators.Gt.valueOf("average").greaterThanValue(50000)).then("富有"), ConditionalOperators.Switch.CaseOperator.when( ComparisonOperators.Gt.valueOf("average").greaterThanValue(10000)).then("中产"), ConditionalOperators.Switch.CaseOperator.when( ComparisonOperators.Gt.valueOf("average").greaterThanValue(7000)).then("低产") ).defaultTo("贫穷")); // 进行更新 mongoTemplate.update(Worker.class) .apply(update) .all(); } }
运行代码,查询 MongoDB 中的文档,数据如下:
/* 1 createdAt:2022/12/29 下午1:09:50*/ { "_id" : ObjectId("63ad211e8f1e061840b1af08"), "name" : "Helen", "age" : 31, "salarys" : [ 5500, 4900, 6200, 5300 ], "average" : 5475, "grade" : "贫穷" }, ....
上面代码对应的 MongoDB 命令如下:
// 向工人集合插入三条数据 db.worker.insertMany( [ { "name": "Helen", "age": 31, "salarys" : [5500, 4900, 6200, 5300] }, { "name": "Tom", "age": 30, "salarys" : [7000, 6900, 7200, 8100] }, { "name": "Joe", "age": 28, "salarys" : [5050, 4980, 5200, 5150] }, ]) // 查看工人集合数据 db.worker.find({}) // 使用聚合管道计算薪水平均值和平均薪水的等级 db.worker.update( { }, [ { $set: { average : { $trunc: [ { $avg: "$salarys" }, 0 ] } } }, { $set: { grade: { $switch: { branches: [ { case: { $gte: [ "$average", 50000 ] }, then: "富有" }, { case: { $gte: [ "$average", 10000 ] }, then: "中产" }, { case: { $gte: [ "$average", 7000 ] }, then: "低产" } ], default: "贫穷" } } } } ], { multi: true } ) // 继续查看计算后的工人集合数据 db.worker.find({})
下面是集合中的数据,如下:
/* 1 createdAt:2022/12/29 下午1:09:50*/ { "_id" : ObjectId("63ad211e8f1e061840b1af08"), "name" : "Helen", "age" : 31, "salarys" : [ 5500, 4900, 6200, 5300 ], "average" : 5475, "grade" : "贫穷" }, /* 2 createdAt:2022/12/29 下午1:09:50*/ { "_id" : ObjectId("63ad211e8f1e061840b1af09"), "name" : "Tom", "age" : 30, "salarys" : [ 7000, 6900, 7200, 8100 ], "average" : 7300, "grade" : "低产" }, /* 3 createdAt:2022/12/29 下午1:09:50*/ { "_id" : ObjectId("63ad211e8f1e061840b1af0a"), "name" : "Joe", "age" : 28, "salarys" : [ 5050, 4980, 5200, 5150 ], "average" : 5095, "grade" : "贫穷" }