如何用Spring Data MongoDB Criteria查询统计DELIVRD状态数据条数
问题:Spring Data MongoDB 实现聚合统计DATA_LIST中DELIVRD状态的数量
现有MongoDB集合结构
{ "_id" : "7959305563", "_class" : "com.loylty.messagingEngine.entities.message.GroupIdDeliveryStatusReport", "DATA_LIST" : [ { "_id" : "7959305562-1", "mobile" : "9566337867", "status" : "DELIVRD", "senttime" : "2018-01-09 14:19:42", "dlrtime" : "2018-01-09 14:57:06", "custom" : "9566337867" }, { "_id" : "7959305562-2", "mobile" : "9566337867", "status" : "DELIVRD", "senttime" : "2018-01-09 14:19:42", "dlrtime" : "2018-01-09 14:57:05", "custom" : "9566337867" }, { "_id" : "7959305562-3", "mobile" : "9566337867", "status" : "DELIVRD", "senttime" : "2018-01-09 14:19:42", "dlrtime" : "2018-01-09 14:57:04", "custom" : "9566337867" } ] }
对应的Java实体类
@Document(collection = "GROUP_ID_DELIVERY_STATUS") public class GroupIdDeliveryStatusReport { @Id @Field("GROUP_ID") private String groupId; @Field("DATA_LIST") private List<SolInfiniGroupIdData> data; public String getGroupId() { return groupId; } public void setGroupId(String groupId) { this.groupId = groupId; } public List<SolInfiniGroupIdData> getData() { return data; } public void setData(List<SolInfiniGroupIdData> data) { this.data = data; } }
需求
统计集合中DATA_LIST数组里status为DELIVRD的元素总数量(示例预期结果为3)。
已验证的Mongo Shell聚合查询
db.getCollection('GROUP_ID_DELIVERY_STATUS').aggregate( { "$unwind" : "$DATA_LIST"}, { "$match" : {"DATA_LIST.status": "DELIVRD" } }, { $group: { _id: null, count: { $sum: 1 } } } )
当前Spring Data MongoDB代码问题
你尝试转换为Spring Data查询,但未正确实现高效的统计逻辑,当前代码如下:
package com.loylty.messagingEngine.service.Impl; import com.loylty.messagingEngine.service.GroupIdDeliveryStatusReportService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.aggregation.AggregationResults; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.stereotype.Service; import org.springframework.data.mongodb.core.aggregation.Aggregation; import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; @Service public class GroupIdDeliveryStatusReportServiceImpl implements GroupIdDeliveryStatusReportService { @Autowired private MongoOperations mongoOperations; @Override public void getDeliveryCount() { Aggregation aggregation = newAggregation( //match(Criteria.where("_id").in("7959305563")), //Use this if you want to do this operation for particular document only unwind("DATA_LIST"), match(Criteria.where("DATA_LIST.status").is("DELIVRD")), project("DATA_LIST._id") ); AggregationResults<String> groupResults = mongoOperations.aggregate(aggregation, "GroupIdDeliveryStatusReport", String.class); int count = groupResults.getMappedResults().size(); } }
正确的Spring Data MongoDB实现方案
你当前用project后统计结果数量的方式虽然能得到结果,但性能较差(会把所有匹配的文档传输到应用层再统计),应该直接在聚合阶段完成统计,和Mongo Shell的逻辑保持一致。
步骤1:创建统计结果的DTO(可选,也可用Map接收)
先创建一个简单类来接收聚合后的统计结果:
public class DeliveryCountResult { private Long count; public DeliveryCountResult() {} public DeliveryCountResult(Long count) { this.count = count; } public Long getCount() { return count; } public void setCount(Long count) { this.count = count; } }
步骤2:修改聚合逻辑
把project替换为group操作,直接在数据库端计算总数:
@Override public Long getDeliveryCount() { Aggregation aggregation = newAggregation( // 如果需要针对特定GROUP_ID查询,解开下面的注释 // match(Criteria.where("_id").is("7959305563")), unwind("DATA_LIST"), match(Criteria.where("DATA_LIST.status").is("DELIVRD")), group().count().as("count") // 对应Mongo Shell中的$group: {_id: null, count: {$sum:1}} ); // 用实体类Class代替硬编码集合名称,和@Document注解保持一致更易维护 AggregationResults<DeliveryCountResult> results = mongoOperations.aggregate( aggregation, GroupIdDeliveryStatusReport.class, DeliveryCountResult.class ); // 获取统计结果,无匹配项时返回0 return results.getUniqueMappedResult() != null ? results.getUniqueMappedResult().getCount() : 0L; }
关键说明
group().count().as("count")完全等价于Mongo Shell中的{ $group: { _id: null, count: { $sum: 1 } } },直接在数据库端完成统计,避免大量数据传输。- 使用
getUniqueMappedResult()而非getMappedResults(),因为聚合后只会返回一个结果文档。 - 若不想额外创建DTO,也可以用
Map接收结果:
AggregationResults<Map> results = mongoOperations.aggregate( aggregation, GroupIdDeliveryStatusReport.class, Map.class ); Map<String, Object> resultMap = results.getUniqueMappedResult(); Long count = resultMap != null ? (Long) resultMap.get("count") : 0L;
内容的提问来源于stack exchange,提问作者Muddassir Rahman
相关产品推荐
相关产品推荐

