Spring Data Mongo聚合中同时使用$trim与$toLower操作的问题排查
解决Spring Data MongoDB聚合中嵌套$trim和$toLower的问题
首先得指出两个关键问题:你的原始MongoDB聚合查询存在语法错误,以及Spring Data MongoDB中嵌套字符串操作的正确写法。
第一步:修正原始MongoDB聚合查询
你的原查询里$group阶段的语法有问题:count字段应该放在$group的对象内部,而且非_id字段必须使用累加器(比如$first、$last),否则MongoDB会报错。修正后的正确查询如下:
db.qabr.aggregate([ { $match: { name: { "$ne": "" } }}, { $group: { _id: { name: { $trim: { input: { $toLower: "$name" } } } }, qabrNumber: { $first: "$qabrNumber" }, // 用$first获取分组内第一个值,根据需求也可以用$push等 qabristaan: { $first: "$qabristaan" }, count: { "$sum": 1 } } }, { $match: { count: { "$gt": 1 } } } ])
第二步:Spring Data MongoDB的正确实现
你之前的代码报错是因为StringOperators.Trim.valueOf()方法无法正确处理嵌套的ToLower操作符。下面提供两种可行的解决方案:
方案1:手动构建AggregationExpression(兼容大多数版本)
直接手动构造嵌套的MongoDB表达式,避免依赖操作符的链式调用可能出现的问题:
import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationExpression; import org.springframework.data.mongodb.core.aggregation.GroupOperation; import org.springframework.data.mongodb.core.aggregation.MatchOperation; import org.springframework.data.mongodb.core.query.Criteria; import org.bson.Document; // 1. 匹配非空name的文档 MatchOperation matchNonEmptyName = Aggregation.match(Criteria.where("name").ne("")); // 2. 分组阶段:手动构造_id的标准化name(先转小写再去空格) GroupOperation groupByNormalizedName = Aggregation.group( AggregationExpression.from(context -> new Document("name", new Document("$trim", new Document("input", new Document("$toLower", "$name") ) ) ) ) ) .first("qabrNumber").as("qabrNumber") .first("qabristaan").as("qabristaan") .count().as("count"); // 3. 筛选出重复项(计数>1) MatchOperation matchDuplicates = Aggregation.match(Criteria.where("count").gt(1)); // 组装并执行聚合 Aggregation aggregation = Aggregation.newAggregation( matchNonEmptyName, groupByNormalizedName, matchDuplicates ); // 替换成你的实体类 AggregationResults<YourQabrEntity> results = mongoTemplate.aggregate(aggregation, "qabr", YourQabrEntity.class);
方案2:使用StringOperators的链式调用(适用于Spring Data MongoDB 3.x+)
如果你的Spring Data MongoDB版本足够新,可以通过trim()方法直接传入toLower()的结果,先新增一个标准化字段再分组,代码更直观:
import org.springframework.data.mongodb.core.aggregation.AddFieldsOperation; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.GroupOperation; import org.springframework.data.mongodb.core.aggregation.MatchOperation; import org.springframework.data.mongodb.core.aggregation.StringOperators; import org.springframework.data.mongodb.core.query.Criteria; // 1. 匹配非空name MatchOperation matchNonEmptyName = Aggregation.match(Criteria.where("name").ne("")); // 2. 新增一个标准化后的name字段:先转小写再去首尾空格 AddFieldsOperation normalizeName = Aggregation.addFields() .addField("normalizedName") .withValue(StringOperators.Trim.trim(StringOperators.ToLower.toLower("name"))) .build(); // 3. 按标准化后的name分组 GroupOperation groupByNormalizedName = Aggregation.group("normalizedName") .first("qabrNumber").as("qabrNumber") .first("qabristaan").as("qabristaan") .count().as("count"); // 4. 筛选重复项 MatchOperation matchDuplicates = Aggregation.match(Criteria.where("count").gt(1)); // 组装聚合 Aggregation aggregation = Aggregation.newAggregation( matchNonEmptyName, normalizeName, groupByNormalizedName, matchDuplicates ); // 执行查询 AggregationResults<YourQabrEntity> results = mongoTemplate.aggregate(aggregation, "qabr", YourQabrEntity.class);
为什么你的原始Spring代码报错?
你之前用StringOperators.Trim.valueOf(StringOperators.ToLower.lowerValueOf("name")),是因为valueOf()方法默认接收的是字段名或简单表达式,无法正确解析嵌套的ToLower操作符,导致生成的MongoDB表达式不符合预期,最终抛出Unrecognized expression '$trim'错误。改用上面的两种方式就能解决这个问题。
内容的提问来源于stack exchange,提问作者aorticDefiance
相关产品推荐
相关产品推荐

