Spring Data MongoDB添加@Aggregation $sample后返回全量文档问题
问题产生原因
- Spring Data MongoDB 对添加了
@Aggregation注解的Repository方法,会完全跳过「根据方法名派生查询条件」的逻辑,只会执行注解中pipeline属性明确声明的聚合管道阶段,不会自动把方法名对应的查询规则追加到管道中。 - 你当前写的聚合管道只有
{$sample:{size:1}}阶段,既没有编写$match阶段过滤typeOfPackageHoliday符合条件的文档,也没有通过占位符引用方法传入的字符串参数。 - 由于传入的方法参数没有被聚合管道引用,你声明的
{$sample:{size:1}}管道会被框架判定为无效管道,最终执行空管道聚合——空聚合管道不会做任何过滤、抽样操作,会直接返回集合内的全部文档,这就是接口返回全量数据的直接原因。 - 之前无参的随机查询方法能正常运行,是因为它不需要绑定参数、不需要前置过滤,单段
$sample管道就能完整满足查询逻辑。
修复方案
- 调整聚合管道顺序,必须先过滤再抽样:聚合管道是按声明顺序执行的,要先加
$match阶段筛选出typeOfPackageHoliday包含指定值的文档,再加$sample阶段从过滤后的结果集中随机取1条。 - 通过索引占位符
?0绑定方法的第一个入参,把参数值传递到聚合管道中,原方法名中Contains对应的MongoDB匹配规则是正则包含匹配,用$regex实现即可。 - 修正后的Repository层方法代码如下:
// 需要AggregationResults包装类时用这个写法 @Aggregation(pipeline = { "{$match: {typeOfPackageHoliday: {$regex: ?0}}}", "{$sample: {size: 1}}" }) AggregationResults<PackageHoliday> findRandomByTypeOfPackageHolidayContains(String typeOfPackageHoliday); // 直接返回单个实体对象时可以去掉包装类,写法更简洁 // @Aggregation(pipeline = { // "{$match: {typeOfPackageHoliday: {$regex: ?0}}}", // "{$sample: {size: 1}}" // }) // PackageHoliday findRandomByTypeOfPackageHolidayContains(String typeOfPackageHoliday);
注意:加了
@Aggregation后方法名不会参与查询生成,不需要严格遵循findByXxx的命名规则,不过建议取语义清晰的方法名方便后续维护。
- Controller层只需要调用修正后的Repository方法即可,不需要额外改动逻辑;如果用了直接返回PackageHoliday的Repository写法,把Controller的返回值对应改成
PackageHoliday就行。
内容的提问来源于stack exchange,提问作者devnoob
相关产品推荐
相关产品推荐

