SpringBoot中MongoRepository聚合查询$Project字段排除失效问题
Spring Boot MongoRepository聚合查询中$project无法排除嵌套字段的解决方案
问题场景
在Spring Boot应用的MongoRepository中编写了如下聚合查询,意图关联t_user集合并排除contact.password和contact.salt字段,但执行后这两个字段仅被设为null而非完全移除;相同语句在Mongo控制台执行时则能成功排除字段。
聚合查询代码:
@Aggregation({ "{$match:{createUid: ?0}}", "{$lookup: {from: 't_user',localField: 'contactUid',foreignField:'_id',as: 'contact'}}", "{$addFields: {contact:{$arrayElemAt: ['$contact',0]} }}", "{$project: { 'contact.password': 0,'contact.salt': 0}}", }) List<HashMap> searchSentAddContacts(ObjectId createUid);
集合结构:t_add_contacts:
[ { "_id": {"$oid": "66ef75795e8146631d0658bc"}, "_class": "org.tzx.xim.entity.AddContactsEntity", "addType": 0, "contactUid": {"$oid": "66ef75505e8146631d0658bb"}, "createTime": {"$date": "2024-09-22T01:40:09.665Z"}, "createUid": {"$oid": "66ef75425e8146631d0658ba"}, "updateTime": {"$date": "2024-09-22T01:40:09.665Z"} } ]
t_user:
[ { "_id": {"$oid": "66ef75425e8146631d0658ba"}, "_class": "org.tzx.xim.entity.UserEntity", "createTime": {"$date": "2024-09-22T01:39:14.569Z"}, "loginName": "user1", "nickName": "haha1", "password": "123456", "salt": "awsddqwdqwd", "updateTime": {"$date": "2024-09-22T01:39:14.569Z"} } ]
解决方案
方案1:在$lookup阶段直接过滤字段(推荐)
将字段过滤逻辑提前到$lookup的pipeline中,避免后续冗余处理,同时提升查询效率:
@Aggregation({ "{$match:{createUid: ?0}}", "{$lookup: {from: 't_user',localField: 'contactUid',foreignField:'_id',as: 'contact', pipeline: [{$project: {password: 0, salt: 0}}]}}", "{$addFields: {contact:{$arrayElemAt: ['$contact',0]} }}" }) List<HashMap> searchSentAddContacts(ObjectId createUid);
此方式在关联t_user时就直接排除了不需要的字段,最终结果中不会出现password和salt。
方案2:使用自定义DTO接收结果
创建仅包含所需字段的DTO类,Spring Data MongoDB会自动忽略无对应映射的字段:
// 主DTO类 public class AddContactWithUserDTO { private ObjectId id; private Integer addType; private ObjectId contactUid; private Date createTime; private ObjectId createUid; private Date updateTime; private UserContactDTO contact; // Getters & Setters // 嵌套的用户信息DTO public static class UserContactDTO { private ObjectId id; private Date createTime; private String loginName; private String nickName; private Date updateTime; // Getters & Setters } }
修改Repository方法的返回类型:
List<AddContactWithUserDTO> searchSentAddContacts(ObjectId createUid);
即使聚合结果中存在password和salt,由于DTO中未定义对应字段,不会被映射到返回结果里。
方案3:在$project中明确指定所有需保留的字段
替代仅排除字段的写法,直接列出所有需要保留的字段,确保不需要的字段被完全移除:
@Aggregation({ "{$match:{createUid: ?0}}", "{$lookup: {from: 't_user',localField: 'contactUid',foreignField:'_id',as: 'contact'}}", "{$addFields: {contact:{$arrayElemAt: ['$contact',0]} }}", "{$project: { _id: 1, addType: 1, contactUid: 1, createTime: 1, createUid: 1, updateTime: 1, 'contact._id': 1, 'contact.createTime': 1, 'contact.loginName': 1, 'contact.nickName': 1, 'contact.updateTime': 1 }}" }) List<HashMap> searchSentAddContacts(ObjectId createUid);
内容的提问来源于stack exchange,提问作者user27406708
相关产品推荐
相关产品推荐

