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

Spring Data MongoRepository @Query查询嵌套列表指定Answer对象问题

问题:根据questionId和answerId从MongoDB查询单个Answer对象

环境与需求

  • 技术栈:Spring + MongoRepository + JDK 11
  • 需求:根据传入的questionId和answerId,从QuestionDocument的answers列表中返回对应的单个Answer对象(包含answerBody、answerAuthor等完整字段信息)

现有代码

QuestionDocument POJO

@Data
@Document(collection = "Questions")
@AllArgsConstructor(onConstructor = @__(@Autowired))
@NoArgsConstructor
public class QuestionDocument {
    @Id
    private String questionId;
    // 其他字段
    private List<Answer> answers;
    // 其他字段
}

Answer POJO

@Data
public class Answer implements Serializable {
    private String answerId;
    // 其他字段(如answerBody、answerAuthor等)
}

QuestionRepository

@Repository
public interface QuestionRepository extends MongoRepository<QuestionDocument, String> {
    @Query(value = "{ { 'questionId' : ?0 }, { 'answers.$answerId' : ?1 } }")
    Answer findByQuestionIdAndAnswerId(String questionId, String answerId);
}

QuestionServiceImpl 相关代码

public Answer getAnswer(String questionId, String answerId){
    return findByQuestionIdAndAnswerId(questionId, answerId);
}

protected Answer findByQuestionIdAndAnswerId(String questionId, String answerId){
    try {
        return questionRepository.findByQuestionIdAndAnswerId(questionId, answerId);
    } catch (Exception e) {
        throw new IllegalArgumentException("There is no answer with this ID.");
    }
}

问题现象

调用接口时响应状态显示成功,但返回的Answer对象所有字段均为null,已确认传入的questionId和answerId是正确的。

已尝试的无效方案

  • 创建类似findByQuestion_Answers_AnswerId(String answerId);的查询方法
  • 在List<Answer> answers上添加@DBRef,并给Answer POJO添加@Document(collection = "Answers")和@Id注解,清空数据库重建数据后查询,仍返回null

解决方案

你的@Query写法存在语法错误,MongoDB的查询条件和投影规则需要分开定义,以下是修正后的实现:

1. 修改QuestionRepository

@Repository
public interface QuestionRepository extends MongoRepository<QuestionDocument, String> {
    @Query(value = "{ 'questionId' : ?0, 'answers.answerId' : ?1 }", 
           fields = "{ 'answers.$' : 1, '_id' : 0 }")
    QuestionDocument findAnswerByQuestionIdAndAnswerId(String questionId, String answerId);
}
  • 查询条件(value):{ 'questionId' : ?0, 'answers.answerId' : ?1 } 匹配questionId正确且answers列表中存在对应answerId的文档
  • 投影规则(fields):{ 'answers.$' : 1, '_id' : 0 } 只返回匹配的单个Answer元素($是MongoDB的位置操作符,指向数组中匹配条件的元素),同时排除默认返回的_id字段

2. 调整Service层逻辑

因为Spring Data MongoDB无法直接将投影后的数组元素映射为单个Answer对象,需要从返回的QuestionDocument中提取目标Answer:

public Answer getAnswer(String questionId, String answerId){
    QuestionDocument doc = findAnswerByQuestionIdAndAnswerId(questionId, answerId);
    if (doc == null || doc.getAnswers() == null || doc.getAnswers().isEmpty()) {
        throw new IllegalArgumentException("There is no answer with this ID.");
    }
    return doc.getAnswers().get(0);
}

protected QuestionDocument findAnswerByQuestionIdAndAnswerId(String questionId, String answerId){
    try {
        return questionRepository.findAnswerByQuestionIdAndAnswerId(questionId, answerId);
    } catch (Exception e) {
        throw new IllegalArgumentException("There is no answer with this ID.");
    }
}

可选:用MongoTemplate实现更灵活的查询

如果希望简化流程,也可以直接使用MongoTemplate进行查询:

@Autowired
private MongoTemplate mongoTemplate;

public Answer getAnswer(String questionId, String answerId) {
    Query query = new Query(Criteria.where("questionId").is(questionId)
                                   .and("answers.answerId").is(answerId));
    query.fields().include("answers.$").exclude("_id");
    
    QuestionDocument doc = mongoTemplate.findOne(query, QuestionDocument.class);
    if (doc == null || doc.getAnswers() == null || doc.getAnswers().isEmpty()) {
        throw new IllegalArgumentException("There is no answer with this ID.");
    }
    return doc.getAnswers().get(0);
}

内容的提问来源于stack exchange,提问作者Tommy A.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 13:40:46