You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级Spring Data Mongo后聚合查询报错且无返回结果

问题描述

我有一个包含match、unwind、match、group和project阶段的MongoTemplate聚合方法,在Spring Data Mongo 2.2.5.Release(Java Driver 3.11.2)下运行正常,但升级至3.3.5(Java Driver 4.4.2)后,抛出BsonInvalidOperationException错误:readObjectId can only be called when the current bson type is object_id and not document。

给内部文档类UE添加@Id注解后错误消失,但聚合查询的映射结果和原始结果都没有返回值。

错误堆栈

readObjectId can only be called when CurrentBSONType is OBJECT_ID, not when CurrentBSONType is DOCUMENT.; nested exception is org.bson.BsonInvalidOperationException: readObjectId can only be called when CurrentBSONType is OBJECT_ID, not when CurrentBSONType is DOCUMENT.
org.springframework.dao.InvalidDataAccessApiUsageException: readObjectId can only be called when CurrentBSONType is OBJECT_ID, not when CurrentBSONType is DOCUMENT.; nested exception is org.bson.BsonInvalidOperationException: readObjectId can only be called when CurrentBSONType is OBJECT_ID, not when CurrentBSONType is DOCUMENT.
    at org.springframework.data.mongodb.core.MongoExceptionTranslator.translateExceptionIfPossible(MongoExceptionTranslator.java:81)
    at org.springframework.data.mongodb.core.MongoTemplate.potentiallyConvertRuntimeException(MongoTemplate.java:2929)
    at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:562)
    at org.springframework.data.mongodb.core.MongoTemplate.doAggregate(MongoTemplate.java:2171)
    at org.springframework.data.mongodb.core.MongoTemplate.doAggregate(MongoTemplate.java:2140)
    at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:2134)
    at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:2026)

实体类代码

AU类(主文档)

@Document(collection = "abc")
public class AU implements Serializable {
    private static final long serialVersionUID = -1025499515231580477L;

    @Id
    @JsonSerialize(using = ObjectIdJsonSerializer.class)
    @JsonDeserialize(using = ObjectIdJsonDeserializer.class)
    public ObjectId id;

    private String applicationUserId;
    private String applicationId;
    private String lastName;
    private String firstName;
    private String middleName;
    private String email;
    private String title;
    private String userType;
    private String lastUpdateBy;
    private Date lastUpdateDate;
    private Date createdTime;
    private Date deletedTime;
    private boolean active;
    private String city;
    private String country;

    private List<UE> uE = new ArrayList<UE>();
}

UE类(内部文档)

public class UE implements Serializable {
    private static final long serialVersionUID = 1L;

    public ObjectId eId;
    private String eName;
    private Date lastRecertDate;
    private String lastRecertBy;
    private Date createdTime;
    private String createdBy;
    private boolean revoked;
    private Date revokedTime;
}

聚合代码

Aggregation aggregation =
        newAggregation(
                match(criteria),
                unwind("ue"),
                match(where("ue.eId").in(eIds)
                       .and("ue.revoked").ne(Boolean.TRUE)),
                group(fields)
                .addToSet("ue").as("ue"),
                project(fields));

已尝试的方案

  • 给UE类的eId字段添加@BsonProperty("id")和@BsonId注解,无效。
  • 调试发现两个版本生成的聚合语句一致,但移除group阶段的.addToSet("ue").as("ue")后,查询可正常运行。

解决方案

这个问题是Spring Data MongoDB 3.x版本对嵌套文档的ObjectId映射逻辑变更导致的,具体解决方法如下:

问题根源

升级后,Spring Data MongoDB在处理聚合结果的嵌套文档时,会默认把名为id的字段识别为文档ID(ObjectId类型),但你的UE类中eId对应MongoDB的字段是eId,聚合通过addToSet("ue")返回的嵌套文档里eId是ObjectId类型,框架映射时出现了类型识别错误。而给UE类加@Id注解后,框架会把eId当作UE文档的ID,但聚合结果里的ue是嵌套数组,框架无法正确映射这种结构,导致无返回值。

1. 修正UE类的字段映射

给UE类的eId字段添加@Field注解明确指定MongoDB字段名,避免框架误识别为文档ID:

public class UE implements Serializable {
    private static final long serialVersionUID = 1L;

    @Field("eId")
    public ObjectId eId;
    // 其他字段保持不变
}

2. 调整聚合的project阶段

确保project阶段明确包含ue字段,避免被过滤:
如果之前的fields变量没包含ue,需要补充进去:

// 基于原有分组字段,添加ue到投影字段中
Fields projectFields = Fields.from(fields).and("ue");
// 替换原project阶段
project(projectFields)

3. 不要给UE类加文档标识注解

UE只是AU的嵌套子文档,不是独立集合的文档,不要给它加@Document或@Id注解。

替代方案(若上述方法无效)

如果聚合映射仍有问题,可以先获取原始Document结果再手动转换为实体类:

AggregationResults<Document> results = mongoTemplate.aggregate(aggregation, "abc", Document.class);
List<AU> auList = results.getMappedResults().stream().map(doc -> {
    AU au = new AU();
    // 手动设置AU的各个字段,例如:
    au.setId(doc.getObjectId("_id"));
    au.setApplicationUserId(doc.getString("applicationUserId"));
    
    // 转换嵌套的ue列表
    List<UE> ueList = ((List<Document>) doc.get("ue")).stream().map(ueDoc -> {
        UE ue = new UE();
        ue.setEId(ueDoc.getObjectId("eId"));
        ue.setEName(ueDoc.getString("eName"));
        // 设置UE其他字段
        return ue;
    }).collect(Collectors.toList());
    au.setuE(ueList);
    return au;
}).collect(Collectors.toList());

内容的提问来源于stack exchange,提问作者GeekCoder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 15:20:33